目录
2019年第十届蓝桥杯国赛B组试题A-平方序列
【问题描述】
小明想找到两个正整数 X 和 Y,满足
-
2019 < X < Y;
-
20192 , X2 , Y2 组成等差数列。
请你求出在所有可能的解中,X + Y 的最小值是多少?
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。
本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:7020
题解 暴力即可,可用等差数列知识适当优化a + c = 2b,∴ c = 2b - a (不过填空题可以但没必要)
#include<bits/stdc++.h>
using namespace std;
int main(){
long long x=2019, y,z;
for(int y=2020; ;y++){
z=sqrt(2*y*y-x*x);
if(z*z==2*y*y - x*x){
cout<<y+z;
break;
}
}
return 0;
}
2019年第十届蓝桥杯国赛B组试题B-质数拆分
【问题描述】
2019可以被分解成若干个两两不同的素数,请问不同的分解方案有多少种?
注意:分解方案不考虑顺序,如 2 + 2017 = 2019 和 2017 + 2 = 2019 属于同一种方案。
答案:55965365465060
题解
01背包:
#include <bits/stdc++.h>
using namespace std;
const int N = 2020;
int dp[N];
int st[N], prim[N];
int k;
//筛一遍素数
void init()
{
st[0] = st[1] = 0;
st[2] = 1;
for (int i = 2; i <= 2019; i++)
{
if (!st[i])
{
prim[k++] = i;
for (int j = i + i; j <= 2019; j += i)
{
st[i] = true;
}
}
}
}
int main()
{
init();
dp[0] = 1;
for (int i = 1; i < k; i++)
{
for (int j = 2019; j >= prim[i]; j--)
{
dp[j] += dp[j-prim[i]];
}
}
cout << dp[2019];
return 0;
}
’
2019年第十届蓝桥杯国赛B组试题D-求值
【问题描述】
学习了约数后,小明对于约数很好奇,他发现,给定一个正整数 t,总是可以找到含有 t 个约数的整数。
小明对于含有 t 个约数的最小数非常感兴趣,并把它定义为 St 。
例如 S1 = 1, S2 = 2, S3 = 4, S4 = 6,···。
现在小明想知道,当 t = 100 时,St 是多少?即 S100 是多少?
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。
本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:45360
题解
暴力
#include<bits/stdc++.h>
using namespace std;
int judge(int n){
int cnt=0;
for(int i=1; i<=n; i++){
if(n%i==0)
cnt++;
}
return cnt;
}
int main(){
for(int i=1; ;i++){
if(judge(i)==100){
cout<<i;
break;
}
}
return 0;
}
2019年第十届蓝桥杯国赛B组试题E-路径计数
【问题描述】
从一个 5 x 5 的方格矩阵的左上角出发,沿着方格的边走,满足以下条件的路线有多少种?
- 总长度不超过 12;
- 最后回到左上角;
- 路线不自交;
- 不走出 5 x 5 的方格矩阵范围之外。
如下图所示,ABC 是三种合法的路线。注意 B 和 C 由于方向不同,所以视为不同的路线。
答案提交
这是一道结果填空的题,你只需要算出结果后提交即可。
本题的结果为一个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。
答案:202
题解
DFS:
#include <bits/stdc++.h>
using namespace std;
int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; //上右下左
int vis[10][10];
int cnt;
void dfs(int x, int y, int step)
{
if (step > 12)
return;
if (x == 0 && y == 0 && step > 2)
{
cnt++;
return;
}
for (int i = 0; i < 4; i++)
{
int a = x + dx[i], b = y + dy[i];
if (a >=0 && a < 6 && b >=0 && b < 6 && !vis[a][b])
{
vis[a][b] = 1;
dfs(a, b, step + 1);
vis[a][b] = 0;
}
}
}
int main()
{
dfs(0, 0, 0);
cout << cnt;
return 0;
}
2019年第十届蓝桥杯国赛B组试题F-最优包含
我们称一个字符串 SS 包含字符串 TT 是指 TT 是 SS 的一个子序列,即可以从字符串 SS 中抽出若干个字符,它们按原来的顺序组合成一个新的字符串与 TT 完全一样。
给定两个字符串 SS 和 TT,请问最少修改 SS 中的多少个字符,能使 SS 包含 TT?
输入格式
输入两行,每行一个字符串。
第一行的字符串为 SS,第二行的字符串为 TT。
两个字符串均非空而且只包含大写英文字母。
输出格式
输出一个整数,表示答案。
数据范围
1≤|T|≤|S|≤10001≤|T|≤|S|≤1000
输入样例:
ABCDEABCD
XAABZ
输出样例:
3
题解
#include<bits/stdc++.h>
using namespace std;
// 解题思路:动态规划。
// dp[i][j]表示令T的前j个字符成为S的前i个字符的子序列需要修改的字符个数。
// 先初始化i=j和j=0的情况。
// 状态转移方程:
// if(s[i]==t[j])
// dp[i][j]=dp[i-1][j-1];
// else
// dp[i][j]=min(dp[i-1][j],dp[i-1][j-1]+1);
const int N = 1e3+10;
int dp[N][N];
string s,t;
int main(){
cin>>s>>t;
int ls= s.size();
int lt= t.size();
if(s[0]!=t[0]){
dp[0][0]=1;
}
//ABCDABCD S
//AABCX T
for (int i = 1; i < lt; i++)
if (s[i] == t[i])
dp[i][i] = dp[i - 1][i - 1];
else
dp[i][i] = dp[i - 1][i - 1] + 1;
for (int i = 1; i < ls; i++)
{
if (s[i] == t[0])
dp[i][0] = 0;
else
dp[i][0] = dp[i - 1][0];
}
for (int j = 1; j < lt; j++)
{
for (int i = j + 1; i < ls; i++)
{
if (s[i] == t[j])
dp[i][j] = dp[i - 1][j - 1]; //21=10 31=20 41=30 32=21 42=31
else
dp[i][j] = min(dp[i - 1][j], dp[i - 1][j - 1] + 1); //变j还是不变j 21=11/10 31=21/20 32=22/21 初始化考虑
}
}
printf("%d\n", dp[ls - 1][lt - 1]);
return 0;
}
2019年第十届蓝桥杯国赛B组试题G-排列数
在一个排列中,一个折点是指排列中的一个元素,它同时小于两边的元素,或者同时大于两边的元素。
对于一个 1∼n1∼n 的排列,如果可以将这个排列中包含 tt 个折点,则它称为一个 t+1t+1 单调序列。
例如,排列 (1,4,2,3)(1,4,2,3) 是一个 33 单调序列,其中 44 和 22 都是折点。
给定 nn 和 kk,请问 1∼n1∼n 的所有排列中有多少个 kk 单调队列?
输入格式
输入一行包含两个整数 n,kn,k。
输出格式
输出一个整数,表示答案。
答案可能很大,你可需要输出满足条件的排列数量除以 123456123456 的余数即可。
数据范围
1≤k≤n≤5001≤k≤n≤500
输入样例:
4 2
输出样例:
12
题解
#include <bits/stdc++.h>
using namespace std;
int n, k;
int a[501];
void init(int n)
{
for (int i = 1; i <= n; i++)
{
a[i] = i;
}
}
int main()
{
cin >> n >> k;
int sum = 0;
init(n);
do
{
int cnt = 0;
for (int i = 2; i <= n-1; i++)
{
if ((a[i] > a[i + 1] && a[i] > a[i - 1]) || (a[i] < a[i + 1] && a[i] < a[i - 1]))
{
cnt++;
}
}
if (cnt+1 == k)
{
sum++;
}
} while (next_permutation(a + 1, a + 1 + n));
cout << sum;
return 0;
}
2018年第九届蓝桥杯国赛B组试题A-换零钞
【问题描述】
x星球的钞票的面额只有:100元,5元,2元,1元,共4种。
小明去x星旅游,他手里只有2张100元的x星币,太不方便,恰好路过x星银行就去换零钱。
小明有点强迫症,他坚持要求200元换出的零钞中2元的张数刚好是1元的张数的10倍,剩下的当然都是5元面额的。
银行的工作人员有点为难,你能帮助算出:在满足小明要求的前提下,最少要换给他多少张钞票吗?
(5元,2元,1元面额的必须都有,不能是0)
【答案提交】
注意,需要提交的是一个整数,不要填写任何多余的内容。
答案:74
题解
暴力:
#include <bits/stdc++.h>
using namespace std;
int main()
{
for(int i=40; i>0; i--)
for (int j = 200; j >0; j--)
{
if((i*5+j*1+j*10*2)==200){
cout<<i+j+j*10<<endl;
break;
}
}
return 0;
}
2018年第九届蓝桥杯国赛B组试题B-激光样式
【问题描述】
x星球的盛大节日为增加气氛,用30台机光器一字排开,向太空中打出光柱。
安装调试的时候才发现,不知什么原因,相邻的两台激光器不能同时打开!
国王很想知道,在目前这种bug存在的情况下,一共能打出多少种激光效果?
显然,如果只有3台机器,一共可以成5种样式,即:
1、全都关上(sorry, 此时无声胜有声,这也算一种)
2、开一台,共3种
3、开两台,只1种
但是30台就不好算了,国王只好请你帮忙了。
【答案提交】
要求提交一个整数,表示30台激光器能形成的样式种数。
注意,只提交一个整数,不要填写任何多余的内容。
答案:2178309
题解
DFS:
#include <bits/stdc++.h>
using namespace std;
int ans;
const int N = 30;
bool st[N];
void dfs(int u)
{
if(u == 30)
{
ans ++;
return;
}
// 每一次都有两种选择
dfs(u + 1); // 1、关闭
if(!st[u - 1]) // 2、打开
{
st[u] = true;
dfs(u + 1);
st[u] = false;
}
}
int main()
{
dfs(0);
cout << ans << endl;
return 0;
}
2018年第九届蓝桥杯国赛B组试题D-调手表
问题描述
小明买了块高端大气上档次的电子手表,他正准备调时间呢。
在 M78 星云,时间的计量单位和地球上不同,M78 星云的一个小时有 n 分钟。
大家都知道,手表只有一个按钮可以把当前的数加一。
在调分钟的时候,如果当前显示的数是 0 ,那么按一下按钮就会变成 1,再按一次变成 2;如果是 n - 1,那么按一次后会变成 0 。
作为强迫症患者,小明一定要把手表的时间调对。如果手表上的时间比当前时间多 1,则要按 n - 1 次加一按钮才能调回正确时间。
小明想,如果手表可以再添加一个按钮,表示把当前的数加 k 该多好啊……
他想知道,如果有了这个 +k 按钮,按照最优策略按键,从任意一个分钟数调到另外任意一个分钟数最多要按多少次。
注意,按 +k 按钮时,如果加 k 后数字超过 n - 1,则会对 n 取模。
比如,n = 10, k = 6 的时候,假设当前时间是 0,连按 2 次 +k 按钮,则调为 2。
输入格式
一行两个整数 n, k,意义如题。
输出格式
一行一个整数,表示按照最优策略按键,从一个时间调到另一个时间最多要按多少次。
样例输入
5 3
样例输出
2
样例解释
如果时间正确则按 0 次。否则要按的次数和操作系列之间的关系如下:
1:+1
2:+1, +1
3:+3
4:+3, +1
数据范围
对于 30% 的数据 0 < k < n ≤ 5
对于 60% 的数据 0 < k < n ≤ 100
对于 100% 的数据 0 < k < n ≤ 105
题解
BFS:
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1005, inf = 1 << 29, mod = 1000000007;
int d[maxn];
void bfs()
{
queue<int> q;
q.push(0);
d[0] = 0;
while (!q.empty())
{
int t = q.front();
q.pop();
int a = (t + 1) % n;
if (d[a] == -1)
{
d[a] = d[t] + 1;
q.push(a);
}
int b = (t + k) % n;
if (d[b] == -1)
{
d[b] = d[t] + 1;
q.push(b);
}
}
}
int main()
{
memset(d, -1, sizeof(d));
cin >> n >> k;
bfs();
int ans = 0;
for (int i = 0; i < n; i++)
ans = max(ans, d[i]);
cout << ans;
return 0;
}
2017年第八届蓝桥杯国赛B组试题A-36进制
【问题描述】
对于16进制,我们使用字母A-F来表示10及以上的数字。
如法炮制,一直用到字母Z,就可以表示36进制。
36进制中,A表示10,Z表示35,AA表示370
你能算出 MANY 表示的数字用10进制表示是多少吗?
【答案提交】
请提交一个整数,不要填写任何多余的内容(比如,说明文字)
答案:1040254
题解一
手算:
22 * 363 + 10 * 362 + 23 * 361 + 34 * 360
题解二
模拟:
#include<bits/stdc++.h>
using namespace std;
int main(){
string s="MANY";
int sum=0;
for(int i=0; i<s.size(); i++){
sum=sum*36+s[i]-'A'+10;
}
cout<<sum;
return 0;
}
2017年第八届蓝桥杯国赛B组试题B-瓷砖样式
问题描述
小明家的一面装饰墙原来是 3 × 10 的小方格。现在手头有一批刚好能盖住 2 个小方格的长方形瓷砖。
瓷砖只有两种颜色:黄色和橙色。小明想知道,对于这么简陋的原料,可以贴出多少种不同的花样来。
小明有个小小的强迫症:忍受不了任何 2 × 2 的小格子是同一种颜色。
瓷砖不能切割,不能重叠,也不能只铺一部分。另外,只考虑组合图案,请忽略瓷砖的拼缝
显然,对于 2 × 3 个小格子来说,口算都可以知道:一共 10 种贴法,如图所示:
但对于 3 ×10 的格子呢?肯定是个不小的数目,请你利用计算机的威力算出该数字。
答案提交
注意:你需要提交的是一个整数,不要填写任何多余的内容(比如:说明性文字)
答案:101466
题解
DFS & set:
解题思路
:
一层一层地铺放瓷砖,铺放完毕后,记录所有合法样式,并用 set
去重。
-
-1
:该位置未放置瓷砖
-
0
:放置的是黄色瓷砖
-
1
:放置的是橙色瓷砖
#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;
set<string> st;
bool judge()
{
for (int i = 1; i < 3; i++)
{
for (int j = 1; j < 10; j++)
{
if ((aa[i][j] + aa[i + 1][j] + aa[i][j + 1] + aa[i + 1][j + 1]) % 4 == 0)
{
return false;
}
}
}
return true;
}
void dfs(int x, int y)
{
if (x == 4)
{
if (judge())
{
string ans = "";
for (int i = 1; i <= 3; i++)
{
for (int j = 1; j <= 10; j++)
{
char *temp;
itoa(aa[i][j], temp, 10);
ans += temp;
}
}
st.insert(ans);
}
return;
}
if (aa[x][y] == -1)
{
//横着放
if (y + 1 <= 10 && aa[x][y + 1] == -1)
{
//两种颜色
for (int i = 0; i < 2; ++i)
{
aa[x][y] = aa[x][y + 1] = i;
if (y + 2 <= 10)
dfs(x, y + 1);
else
dfs(x + 1, 1);
aa[x][y] = aa[x][y + 1] = -1;
}
}
//竖着放
if (x + 1 <= 3 && aa[x + 1][y] == -1)
{
for (int i = 0; i < 2; i++)
{
aa[x][y] = aa[x + 1][y] = i;
if (y + 1 <= 10)
dfs(x, y + 1);
else
dfs(x + 1, 1);
aa[x][y] = aa[x + 1][y] = -1;
}
}
}
else
{
if (y == 10)
dfs(x + 1, 1);
else
dfs(x, y + 1);
}
}
int main()
{
memset(aa, -1, sizeof(aa));
dfs(1, 1);
cout << st.size() << endl;
return 0;
}
2017年第八届蓝桥杯国赛B组试题E-对局匹配
问题描述
小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。
小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是 K 的两名用户匹配在一起。
如果两人分差小于或大于 K,系统都不会将他们匹配。
现在小明知道这个网站总共有 N 名用户,以及他们的积分分别是 A1, A2, … AN。
小明想了解最多可能有多少名用户同时在线寻找对手,但是系统却一场对局都匹配不起来 (任意两名用户积分差不等于 K)?
输入格式
第一行包含两个个整数 N 和 K。
第二行包含N个整数 A1, A2, … AN。
输出格式
一个整数,代表答案。
样例输入1
10 0
1 4 2 8 5 7 1 4 2 8
样例输出1
6
样例输入2
10 1
2 1 1 1 1 4 4 3 4 4
样例输出2
8
数据范围
对于 30% 的数据,1 ≤ N ≤ 10
对于 100% 的数据,1 ≤ N ≤ 105, 0 ≤ Ai ≤ 105, 0 ≤ K ≤ 105
题解一
DFS(会超时)
#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], s[maxn], st[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;
bool judge(int u, int i)
{
for (int j = 1; j < u; j++)
{
if (a[j] - s[i] == k || s[i] - a[j] == k)
return false;
}
return true;
}
void dfs(int u)
{
m = max(m, u - 1);
for (int i = 1; i <= n; i++)
{
if (st[i] || !judge(u, i))
continue;
st[i] = true;
a[u] = s[i];
dfs(u + 1);
st[i] = false;
}
return ;
}
int main()
{
cin >> n >> k;
for (int i = 1; i <= n; i++)
{
cin >> s[i];
}
dfs(1);
cout<<m;
return 0;
}
题解二
动态规划:
以第二个数据 2 1 1 1 1 4 4 3 4 4
为例,由于 k = 1,所以分成 1 组公差为 1 等差数列。
等差数列
:0 1 2 3 4 …出现次数
:0 4 1 1 4 …
决策
:由于相邻两项公差为 k,所以不能选择相邻的两项。
状态转移方程:
当 i == 0
,f[i] = s[i]
当 i == 1
,f[i] = max(f[i - 1], s[i])
当 i >= 2
,f[i] = max(f[i - 1], f[i - 2] + s[i])
#include <iostream>
using namespace std;
const int N = 100010;
int n, k, x, ans;
int cnt[N], s[N], f[N];
int main()
{
cin >> n >> k;
for (int i = 1; i <= n; i ++)
{
cin >> x;
cnt[x] ++; // 统计每个积分出现的次数
}
if(k == 0) // k = 0 需要特判
{
for (int i = 0; i < N; i ++)
if(cnt[i]) ans ++;
}
else
{
for (int i = 0; i < k; i ++) // 分成 k 个公差为 k 的等差数列
{
int u = 0;
for (int j = i; j <= N; j += k) // 首项分别为 i 出现的次数
s[u ++] = cnt[j];
f[0] = s[0];
for (int j = 1; j < u; j ++)
{
if(j == 1) f[j] = max(f[j - 1], s[j]);
else f[j] = max(f[j - 1], f[j - 2] + s[j]);
}
ans += f[u - 1]; // 加上每组等差数列选择的最大值
}
}
cout << ans << endl;
return 0;
}
2016年第七届蓝桥杯国赛B组试题A-一步之遥
问题描述】
从昏迷中醒来,小明发现自己被关在X星球的废矿车里。
矿车停在平直的废弃的轨道上。
他的面前是两个按钮,分别写着“F”和“B”。
小明突然记起来,这两个按钮可以控制矿车在轨道上前进和后退。
按F,会前进97米。按B会后退127米。
透过昏暗的灯光,小明看到自己前方1米远正好有个监控探头。
他必须设法使得矿车正好停在摄像头的下方,才有机会争取同伴的援助。
或许,通过多次操作F和B可以办到。
矿车上的动力已经不太足,黄色的警示灯在默默闪烁…
每次进行 F 或 B 操作都会消耗一定的能量。
小明飞快地计算,至少要多少次操作,才能把矿车准确地停在前方1米远的地方。
请填写为了达成目标,最少需要操作的次数。
【答案提交】
注意,需要提交的是一个整数,不要填写任何无关内容(比如:解释说明等)
答案:97
题解
枚举:
#include <iostream>
using namespace std;
int main()
{
for (int i = 1; i < 100; i ++)
for (int j = 1; j < 100; j ++)
if(i * 97 - j * 127 == 1) cout << i + j << endl;
return 0;
}
2016年第七届蓝桥杯国赛B组试题B-凑平方数
问题描述
把 0 ~ 9 这 10 个数字,分成多个组,每个组恰好是一个平方数,这是能够办到的。
比如:0,36,5948721
再比如:{1098524736};{1,25,6390784};{0,4,289,15376} 等等…
注意,0 可以作为独立的数字,但不能作为多位数字的开始。
分组时,必须用完所有的数字,不能重复,不能遗漏。
如果不计较小组内数据的先后顺序,请问有多少种不同的分组方案?
答案提交
注意:需要提交的是一个整数,不要填写多余内容。
答案:300
题解
DFS:
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
LL used[10], tmp[10], s[10], k;
set<string> cnt;
void dfs(int u)
{
if (u == 10)
{
copy(used, used + k, tmp); // 将 used 数组中的内容,复制给 tmp 数组
sort(tmp, tmp + k);
string ans = "";
for (int i = 0; i < k; i++)
{
char s[20];
sprintf(s, "%lld", tmp[i]); // 将数字转换成字符串
ans += s;
ans += '-'; // 每个平方数都以 '-' 隔开
}
cnt.insert(ans); // 存储答案,并去重
return;
}
if (s[u] == 0) // 0 不能作为数字的开头,自成一组
{
used[k++] = 0;
dfs(u + 1);
used[--k] = 0;
}
else
{
LL t = 0;
for (int i = u; i < 10; i++)
{
t = t * 10 + s[i];
LL x = sqrt(t);
if (x * x == t)
{
used[k++] = t;
dfs(i + 1);
used[--k] = 0;
}
}
}
}
int main()
{
for (int i = 0; i < 10; i++)
s[i] = i;
do
{
dfs(0);
k = 0;
} while (next_permutation(s, s + 10));
cout << cnt.size() << endl;
return 0;
}
练习题:星期一
整个20世纪(1901年1月1日至2000年12月31日之间),一共有多少个星期一?(不要告诉我你不知道今天是星期几)
以下程序实现了这一功能,请你补全以下空白处内容:
提示:
判断1901年1月1日到2000年12月31的每一天是星期几,如果是星期一则统计的个数+1。
#include <stdio.h>
int main()
{
int year, day, dayrun = 0, dayping = 0, sumday = 0;
int count = 0;
for (year = 1901; year <= 2000; year++)
{
if ((year % 4 == 0 && year % 100 != 0) || (year % 400 == 0))
{
dayrun += 366;
}
else
{
dayping += 365;
}
}
sumday = dayrun + dayping;
__________________________;
printf("%d", count);
return 0;
}
A | for (day = 2; day <= sumday - 7; day++) { count++; } |
B | for (day = 2; day <= sumday - 7; day += 7) { count++; } |
C | for (day = 2; day < sumday; day += 7) { count++; } |
D | for (day = 2; day <= sumday; day += 7) { count++; } |
答案:
for (day = 2; day <= sumday - 7; day += 7)
{
count++;
}