训练试题目录
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 为例)
:
123
:1 入栈,1 弹出;2 入栈,2 弹出;3 入栈,3 弹出。132
:1 入栈,1 弹出;2 入栈,3 入栈;3 弹出,2 弹出。213
:1 入栈,2 入栈;2 弹出,1 弹出;3 入栈,3 弹出。231
:1 入栈,2 入栈;2 弹出;3 入栈,3 弹出;1 弹出。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;
}
}
}
}