蓝桥杯C++国赛最后一周训练(2)

2015年第六届蓝桥杯国赛B组试题A-积分之谜

【问题描述】

小明开了个网上商店,卖风铃。共有3个品牌:A,B,C。
为了促销,每件商品都会返固定的积分。
小明开业第一天收到了三笔订单:
第一笔:3个A + 7个B + 1个C,共返积分:315
第二笔:4个A + 10个B + 1个C,共返积分:420
第三笔:A + B + C,共返积分…
你能算出第三笔订单需要返积分多少吗?

【答案提交】
请提交该整数,不要填写任何多余的内容。


答案:105


题解
枚举:

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1005, inf = 1 << 29, mod = 1000000007;
const double pi = acos(-1.0);
typedef long long ll;;
inline int gcd(int x,int y) { if(!x) return y; return gcd(y%x,x); }
int n, m, k, max_sum, min_sum;
int a[maxn], b[maxn];
int aa[maxn][maxn], bb[maxn][maxn], dp[maxn][maxn];
int dir[][2] = {{0, 1}, {-1, 0}, {0, -1}, {1, 0}, {-1, 1}, {-1, -1}, {1, -1}, {1, 1}};
vector<int> vi;

int main()
{
   int x,y,z;
   for(int i=1; i<200; i++){
       for(int j=1; j<200; j++){
           for(int k=1; k<200; k++){
               if(((3*i + 7*j +k)==315) &&((4*i + 10*j + 1*k)==420)){
                   cout<<i+j+k;
                   return 0;
               } 
           }
       }
   }
   return 0;
}


2015年第六届蓝桥杯国赛B组试题B-完美正方形

问题描述
如果一些边长互不相同的正方形,可以恰好拼出一个更大的正方形,则称其为完美正方形。

历史上,人们花了很久才找到了若干完美正方形。

比如:如下边长的 22 个正方形:2 3 4 6 7 8 12 13 14 15 16 17 18 21 22 23 24 26 27 28 50 60

如图这样组合,就是一种解法。
在这里插入图片描述

此时,紧贴上边沿的是:60 50,紧贴下边沿的是:26 28 17 21 18

22 阶完美正方形一共有8种。

右边的组合是另一种:2 5 9 11 16 17 19 21 22 24 26 30 31 33 35 36 41 46 47 50 52 61

如果告诉你该方案紧贴着上边沿的是从左到右依次为:47 46 61,你能计算出紧贴着下边沿的是哪几个正方形吗?

答案提交
请提交紧贴着下边沿的正方形的边长,从左到右,用空格分开。
不要填写任何多余的内容或说明文字。


答案:50 33 30 41


题解
DFS:

∵ 边长:47 + 16 + 61 = 154
∴ 将该正方形看成一个 154 × 154 的网格即可

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1005, inf = 1 << 29, mod = 1000000007;

//47 + 16 + 61 = 154
int rect[22] = {2, 5, 9, 11, 16, 17, 19, 21, 22, 24, 26, 30, 31, 33, 35, 36, 41, 46, 47, 50, 52, 61};
int used[100], op[100];

int m[155][155];

void add(int x, int y, int l, int t)
{


    for (int i = x; i < x + l; i++)
    {
        for (int j = y; j < y + l; j++)
        {
            m[i][j] = t;
        }
    }
}

bool judge(int x, int y, int l)
{
    if (x + l - 1 > 154 || y + l - 1 > 154)
        return false; // 出界

    for (int i = x; i < x + l; i++)
    {
        for (int j = y; j < y + l; j++)
        {
            if (m[i][j])
            {
                return false;
            }
        }
    }
    return true;
}

void dfs(int x, int y)
{
    if (y == 154)
        x++, y = 1;

    if (x == 155)
    {
        for (int i = 1; i <= 154; i++)
        {
            if (!op[m[154][i]]){
                cout<<m[154][i]<<" ";
                op[m[154][i]]=1;
            }
        }
        return ;
    }

    if (!m[x][y])
    {
        for (int i = 0; i < 22; i++)
        {
            if (!used[rect[i]])
            {
                if (x > 0 && x <= 154 && y > 0 && y <= 154 && judge(x, y, rect[i]))
                {
                    used[rect[i]] = 1;
                    add(x, y, rect[i], rect[i]);
                    dfs(x, y + 1);
                    add(x, y, rect[i], 0);
                    used[rect[i]] = 0;
                }
            }
        }
    }
    else
    {
        dfs(x, y + 1);
    }
}

int main()
{
    add(1, 1, 47, 47);
    add(1, 48, 46, 46);
    add(1, 94, 61, 61);
    used[47] = used[46] = used[61] = 1;
    dfs(1, 1);
    return 0;
}

2015年第六届蓝桥杯国赛B组试题D-密文搜索

问题描述
福尔摩斯从 X 星收到一份资料,全部是小写字母组成。

他的助手提供了另一份资料:许多长度为 8 的密
码列表。

福尔摩斯发现,这些密码是被打乱后隐藏在先前那份资料中的。

请你编写一个程序,从第一份资料中搜索可能隐藏密码的位置。要考虑密码的所有排列可能性。

输入格式
输入第一行:一个字符串 s,全部由小写字母组成,长度小于 1024*1024
紧接着一行是一个整数 n,表示以下有 n 行密码,1 ≤ n ≤1000
紧接着是 n 行字符串,都是小写字母组成,长度都为 8

输出格式
一个整数, 表示每行密码的所有排列在 s 中匹配次数的总和。

样例输入
aaaabbbbaabbcccc
2
aaaabbbb
abcabccc

样例输出
4

解释
第一个密码匹配了3次,第二个密码匹配了1次,一共4次。


题解:

首先记录所有长度为 8 的字母序列,然后求出每次输入密码的全排列,对于每一种排列,查询是否已被记录

#include <bits/stdc++.h>
using namespace std;

const int maxn = 1005, inf = 1 << 29, mod = 1000000007;
const double pi = acos(-1.0);
typedef long long ll;
;
inline int gcd(int x, int y)
{
    if (!x)
        return y;
    return gcd(y % x, x);
}
int n, m, k, max_sum, min_sum;
int a[maxn], b[maxn];
int aa[maxn][maxn], bb[maxn][maxn], dp[maxn][maxn];
int dir[][2] = {{0, 1}, {-1, 0}, {0, -1}, {1, 0}, {-1, 1}, {-1, -1}, {1, -1}, {1, 1}};
vector<int> vi;

map<string, int> mp;

int main()
{
    string s;
    cin >> s;

    for (int i = 0; i < s.size() - 7; i++)
    {
        string temp = s.substr(i, 8);
        cout<<temp<<"  ";
        mp[temp]++;
    }

    int ans = 0;

    cin >> n;
    while (n--)
    {
        string x;
        cin >> x;
        sort(x.begin(), x.end());

        do
        {
            //cout << x << " " << ans << endl;
            if (mp.count(x))
                ans+=mp.count(x);

        } while (next_permutation(x.begin(), x.end()));
    }

    cout << ans << endl;
    return 0;
}

2014年第五届蓝桥杯国赛B组试题A-年龄巧合

问题描述】

小明和他的表弟一起去看电影,有人问他们的年龄。小明说:今年是我们的幸运年啊。
我出生年份的四位数字加起来刚好是我的年龄。表弟的也是如此。已知今年是2014年,并且,小明说的年龄指的是周岁。
请推断并填写出小明的出生年份。

【答案提交】
这是一个4位整数,请通过浏览器提交答案,不要填写任何多余的内容(比如,他表弟的出生年份,或是他们的年龄等等)


答案:1988


题解
枚举:

#include<bits/stdc++.h>
using namespace std;

int cnt(int x){
    int ans=0;
    while(x){
        ans+=x%10;
        x/=10;
    }
    return ans;
}

int main(){
    for(int i=1900; i<3000; i++){
        if(cnt(i)==2014-i){
            cout<<i;
            return 0;
        }
    }
    return 0;
}

2014年第五届蓝桥杯国赛B组试题D-出栈次序

问题描述
X 星球特别讲究秩序,所有道路都是单行线。

一个甲壳虫车队,共 16 辆车,按照编号先后发车,夹在其它车流中,缓缓前行。

路边有个死胡同,只能容一辆车通过,是临时的检查站,如图所示。

X星球太死板,要求每辆路过的车必须进入检查站,也可能不检查就放行,也可能仔细检查。

如果车辆进入检查站和离开的次序可以任意交错。那么,该车队再次上路后,可能的次序有多少种?

为了方便起见,假设检查站可容纳任意数量的汽车。

显然,如果车队只有1辆车,可能次序 1 种;2 辆车可能次序 2 种;3 辆车可能次序 5 种。

现在足足有 16 辆车啊,亲!需要你计算出可能次序的数目。

在这里插入图片描述

答案提交
这是一个整数,请通过浏览器提交答案,不要填写任何多余的内容(比如说明性文字)。


答案:35357670


题解
卡特兰数:

看到这样的死胡同,很容易想到一种数据结构——,那么进站相当于push,出站相当于pop

样例解释 (以 3 为例)

  1. 123:1 入栈,1 弹出;2 入栈,2 弹出;3 入栈,3 弹出。
  2. 132:1 入栈,1 弹出;2 入栈,3 入栈;3 弹出,2 弹出。
  3. 213:1 入栈,2 入栈;2 弹出,1 弹出;3 入栈,3 弹出。
  4. 231:1 入栈,2 入栈;2 弹出;3 入栈,3 弹出;1 弹出。
  5. 321:1 入栈,2 入栈,3 入栈;3 弹出,2 弹出,1 弹出。

可以发现,当数量为 3 时,入栈和出栈的次数都是 3,且无论是第几次操作,push 的次数一定大于等于 pop 的次数。

这时候可以用 卡特兰数 来求解或者常规DFS。

#卡特兰数解法
#include <iostream>
using namespace std;

int c[50][50];

int main()
{

<!-- -->
    for (int i = 0; i <= 16 \* 2; i ++)
        for (int j = 0; j <= i; j ++)
            if(!j) c[i][j] = 1;
            else c[i][j] = c[i - 1][j - 1] + c[i - 1][j];

    cout << c[16 \* 2][16] / (16 + 1) << endl;
    return 0;
}
#DFS
#inclu
de <bits/stdc++.h>
using namespace std;

int num = 0;

void dfs(int n, int r, int l)
{
    if (n == 32)
    {
        num++;
        return;
    }

    if (r > l)
    {
        if (r < 16)
            dfs(n + 1, r + 1, l);
        if (l < 16)
            dfs(n + 1, r, l + 1);
    }
    else
    {
        if (r == l)
            if (r < 16)
                dfs(n + 1, r + 1, l);
    }

    return;
}

int main()
{

    dfs(0, 0, 0);
    cout << num << endl;
    return 0;
}

2014年第五届蓝桥杯国赛B组试题D-生物芯片

问题描述
X 博士正在研究一种生物芯片,其逻辑密集度、容量都远远高于普通的半导体芯片。

博士在芯片中设计了 n 个微型光源,每个光源操作一次就会改变其状态,即:点亮转为关闭,或关闭转为点亮。

这些光源的编号从 1 到 n,开始的时候所有光源都是关闭的。

博士计划在芯片上执行如下动作:

所有编号为 2 的倍数的光源操作一次,也就是把 2 4 6 8 … 等序号光源打开

所有编号为 3 的倍数的光源操作一次, 也就是对 3 6 9 … 等序号光源操作,注意此时 6 号光源又关闭了。

所有编号为 4 的倍数的光源操作一次。

直到编号为 n 的倍数的光源操作一次。

X博士想知道:经过这些操作后,某个区间中的哪些光源是点亮的。

输入格式
3 个用空格分开的整数:N,L,R (L < R < N < 1015)
N 表示光源数,L 表示区间的左边界,R 表示区间的右边界。

输出格式
输出 1 个整数,表示经过所有操作后,[L, R] 区间中有多少个光源是点亮的。

样例输入1
5 2 3

样例输出1
2

样例输入2
10 3 6

样例输出2
3


题解一
刷数组:
会超时,而且数组也开不了这么大

#include <iostream>
using namespace std;

bool st[1000010];

int main()
{
	int n, l, r;
	cin >> n >> l >> r;

	int ans = 0;
	for (int i = 2; i <= n; i ++)
		for (int j = i; j <= r; j += i) 
		{
			if(st[j]) st[j] = false, ans --;
			else st[j] = true, ans ++;
		}

	for (int i = 0; i < l; i ++) if(st[i]) ans --;	

	cout << ans << endl;
	return 0;
}

题解二
完全平方数:

定义:若 a = b2,则 a 就是一个完全平方数(a,b 均为整数)

有题意可得,只有操作奇数次,该光源才会亮,即有奇数个因子,而完全平方数就拥有奇数个因子

以 9 为例:1、3、9

  • 1 × 9
  • 3 × 3
  • 9 × 1

但是题目排除了 1 这个因子,所以完全平方数的因子数量变为偶数,而非完全平方数的因子数量变为奇数

∴ 我们要求的是 非完全平方数的数量,即 总数 - 完全平方数的数量

#include <iostream>
#include <cmath>
using namespace std;

typedef long long LL;

int main()
{
	LL n, l, r;
	cin >> n >> l >> r;

	LL total = r - l + 1;

	l = sqrt(l - 1);                   // 排除边界恰好是完全平方数的情况
	r = sqrt(r);

	cout << total - (r - l) << endl;
	return 0;
}

2014年第五届蓝桥杯国赛B组试题D-Log大侠

问题描述
atm 参加了速算训练班,经过刻苦修炼,对以 2 为底的对数算得飞快,人称 Log 大侠。

一天,Log 大侠的好友 drd 有一些整数序列需要变换,Log 大侠正好施展法力…

变换的规则是: 对其某个子序列的每个整数变为: [log2 x + 1] 其中 [] 表示向下取整

就是对每个数字求以 2 为底的对数,然后取下整。

例如对序列 3 4 2 操作一次后,这个序列会变成 2 3 2。

drd 需要知道,每次这样操作后,序列的和是多少。

输入格式
第一行两个正整数 n,m 。
第二行 n 个数,表示整数序列,都是正数。
接下来 m 行,每行两个数 L,R 表示 atm 这次操作的是区间 [L, R],数列序号从 1 开始。

输出格式
输出 m 行,依次表示 atm 每做完一个操作后,整个序列的和。

样例输入
3 3
5 6 4
1 2
2 3
1 3

样例输出
8
6

数据范围
对于 30% 的数据, n, m ≤ 103
对于 100% 的数据, n, m ≤ 105


题解
位运算:

1 << x:等价于 2x

#include <iostream>
using namespace std;

int a[100010];

int main()
{
	int n, m;
	cin >> n >> m;

	for (int i = 1; i <= n; i ++) cin >> a[i];

	while(m --)
	{
		int l, r;
		cin >> l >> r;

		for (int i = l; i <= r; i ++)
			for (int j = 0; j <= 20; j ++)
				if(1 << j > a[i])
				{
				 	a[i] = j;
				 	break;
				 }

		int ans = 0;
		for (int i = 1; i <= n; i ++) ans += a[i];

		cout << ans << endl;
	}

	return 0;
}

ps:按理说,极限情况下 n * m = 1010,居然还是过了

题解二
库函数:

#include <iostream>
#include <cmath>
using namespace std;

int a[100010];

int main()
{
	int n, m;
	cin >> n >> m;

	int ans = 0;
	for (int i = 1; i <= n; i ++) 
	{
		cin >> a[i];
		ans += a[i];                          // 类似于前缀和
	}

	while(m --)
	{
		int l, r;
		cin >> l >> r;

		for (int i = l; i <= r; i ++)
		{
			ans -= a[i];
		 	a[i] = log2(a[i]) + 1;
		 	ans += a[i];
		}

		cout << ans << endl;
	}

	return 0;
}

2013年第四届蓝桥杯国赛B组试题A-猜灯谜

【问题描述】

A 村的元宵节灯会上有一迷题:
请猜谜 * 请猜谜 = 请边赏灯边猜
小明想,一定是每个汉字代表一个数字,不同的汉字代表不同的数字。
请你用计算机按小明的思路算一下,然后提交“请猜谜”三个字所代表的整数即可。

【答案提交】
请严格按照格式,通过浏览器提交答案。
注意:只提交一个3位的整数,不要写其它附加内容,比如:说明性的文字。


答案:897


题解
DFS:

#include <iostream>
#include <algorithm>
using namespace std;

int a[10], st[10];

void dfs(int u)
{
    if(u == 10)
    {
        int x = a[0] * 100 + a[4] * 10 + a[5];
        int y = a[0] * 100000 + a[1] * 10000 + a[2] * 1000 + a[3] * 100 + a[1] * 10 + a[4];

        if(a[0] != 0 && x * x == y) cout << x << endl;

        return;
    }

    for (int i = 0; i < 10; i ++)
        if(!st[i]) 
        {
            a[u] = i;

            st[i] = true;
            dfs(u + 1);
            st[i] = false;
        }
}

int main()
{
    dfs(0);
    return 0;
}

ps:用 next_permutation 函数代码会更短,懒得写了。

2013年第四届蓝桥杯国赛B组试题B-连续奇数和

【问题描述】

小明看到一本书上写着:任何数字的立方都可以表示为连续奇数的和。
比如:
23 = 8 = 3 + 5
33 = 27 = 7 + 9 + 11
43 = 64 = 1 + 3 + … + 15
虽然他没有想出怎么证明,但他想通过计算机进行验证。
请你帮助小明写出 111 的立方之连续奇数和表示法的起始数字。如果有多个表示方案,选择起始数字小的方案。

【答案提交】
请严格按照要求,通过浏览器提交答案。
注意:只提交一个整数,不要写其它附加内容,比如:说明性的文字。


答案:371


题解
枚举:

#include <iostream>
using namespace std;

int main()
{
	int n = 111 * 111 * 111;

	for (int i = 1; i < n; i += 2)
	{
		int ans = 0;
		for (int j = i; j < n; j += 2)
		{
			ans += j; 
			if(ans == n) 
			{
			    cout << i << endl;
			    return 0;
			}
		}
	} 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值