为了更好的备战蓝桥杯,笔者将按照每日更新两道题的速度,争取在赛前刷穿真题,同样希望本文也可以帮助到大家。
2017 年 省赛填空题 迷宫
答案 = 31
简单 dfs
AC code
#include<bits/stdc++.h>
using namespace std;
char g[15][15];
bool vis[15][15];
bool dfs(int i,int j)
{
if(i < 1|| i > 10 ||j < 1|| j > 10) return true;
if(vis[i][j]) return false;
vis[i][j] = true;
if(g[i][j] == 'U') dfs(i - 1,j);
else if(g[i][j] == 'D') dfs(i + 1,j);
else if(g[i][j] == 'L') dfs(i,j - 1);
else if(g[i][j] == 'R') dfs(i,j + 1);
}
int main()
{
for(int i = 1;i <= 10;i ++)
for(int j = 1;j <= 10;j ++)
cin >> g[i][j];
int cnt = 0;
for(int i = 1;i <= 10;i ++)
for(int j = 1;j <= 10;j ++)
{
memset(vis,false,sizeof vis);
if(dfs(i,j)) cnt ++;
}
cout << cnt << endl;
return 0;
}
2017 年 省赛填空题 跳蚱蜢
答案 = 20
类似八数码问题,想要将012345678 => 087654321
每次跳跃其实就是和 0 交换位置,每次的转移为 -2,-1,1,2 ,那么负数转移的时候可以注意到
可以通过 (i + 9) % 9 的方式滚动数组模拟环,解决了这些问题就可以开始愉快的bfs了
#include<bits/stdc++.h>
using namespace std;
struct node
{
string str;
int pos;
int step;
};
queue<node> q;
set<string> vis;
void change(node now,int key)
{
string s = now.str;
swap(s[now.pos],s[(now.pos + key + 9) % 9]);
if(vis.count(s) == 0)
{
vis.insert(s);
q.push({s,(now.pos + key + 9) % 9,now.step + 1});
}
}
int main()
{
string start = "012345678";//初始状态
q.push({start,0,0});
while(q.size())
{
auto t = q.front();
if(t.str == "087654321")//目标状态
{
cout << t.step << endl;
break;
}
else
{
//将一层全都拓展完以后 再弹出
change(t,-2);
change(t,-1);
change(t,1);
change(t,2);
q.pop();
}
}
return 0;
}
2017 年 省赛填空题 方格分割
答案 = 509
我们可以注意到倘若我们要切成完全相同的图形那么切割的路线必定是会经过 (3,3) 点的,因为他是一个中心对称图形。
而且一个图形要被切断的条件是到达了这个6 * 6方块的边界,因为我们只需要从 (3,3) 开始 dfs ,碰到边界即存在一个合法方案,因为一个点抵达了边界,他的对称点必然也在边界之上,必定能将图形切断。然后最后要注意的是需要将答案除 4 ,因为中心对称的缘故会存在重复,比如说一直向上,向下,向左,向右切出来的图形是一样的。
#include<bits/stdc++.h>
using namespace std;
int cnt = 0;
const int N = 10;
int g[N][N];
int dx[] = {1,-1,0,0};
int dy[] = {0,0,-1,1};
void dfs(int i,int j)
{
if(i == 0 || i == 6 || j == 0 || j == 6)
{
cnt ++;
return ;
}
for(int k = 0;k < 4;k ++)
{
int nx = i + dx[k];
int ny = j + dy[k];
if(g[nx][ny] == 0)
{
g[nx][ny] = 1;
g[6 - nx][6 - ny] = 1;
dfs(nx,ny);
g[nx][ny] = 0;
g[6 - nx][6 - ny] = 0;
}
}
}
int main()
{
memset(g,0,sizeof g);
g[3][3] = 1;
dfs(3,3);
cout << cnt / 4 << endl;
return 0;
}
2017 年 省赛代码填空题 字母组串
运用 dp 的思想,长度为 n 的包含 a 个 A,b 个 B,c 个 C 字母的答案一定是会从
1.长度为 n 的包含 a - 1 个 A,b 个 B,c 个 C 字母的答案
2.长度为 n 的包含 a 个 A,b - 1 个 B,c 个 C 字母的答案
3.长度为 n 的包含 a 个 A,b 个 B,c - 1 个 C 字母的答案
转移而来的,因此把 dp 的转移按递归写法写就成了
return f(a - 1,b,c,n - 1) + f(a,b - 1,c,n - 1) + f(a,b,c - 1,n - 1);
AC code
#include <stdio.h>
// a 个 A,b 个 B,c 个 C 字母,能组成多少个不同的长度为n的串。
int f(int a, int b, int c, int n)
{
if(a < 0 || b < 0 || c < 0) return 0;
if(n == 0) return 1;
return f(a - 1,b,c,n - 1) + f(a,b - 1,c,n - 1) + f(a,b,c - 1,n - 1);
}
int main()
{
printf("%d\n", f(1,1,1,2));
printf("%d\n", f(1,2,3,3));
return 0;
}
2017 年 省赛代码填空题 字母组串
思路
题目提示了用矩阵的思想解决,那么我们就将 s1 和 s2 作为行列
ex.
b a a b c d a d a b c
a 0 1 1 0 0 0 1 0 1 0 0
b 1 0 0 2 0 0 0 0 0 2 0
c 0 0 0 0 3 0 0 0 0 0 3
d 0 0 0 0 0 4 0 1 0 0 0
k 0 0 0 0 0 0 0 0 0 0 0
k 0 0 0 0 0 0 0 0 0 0 0
k 0 0 0 0 0 0 0 0 0 0 0
很容易就可以想到若 匹配结果,那必然是 匹配上的结果再
即串在第 位和 的第 位匹配上了,那么匹配数必定是 前 位和 的 的匹配情况
AC code
#include <stdio.h>
#include <string.h>
#define N 256
int f(const char* s1, const char* s2)
{
int a[N][N];
int len1 = strlen(s1);
int len2 = strlen(s2);
int i,j;
memset(a,0,sizeof(int)*N*N);
int max = 0;
for(i=1; i<=len1; i++){
for(j=1; j<=len2; j++){
if(s1[i-1]==s2[j-1]) {
a[i][j] = a[i - 1][j - 1];
if(a[i][j] > max) max = a[i][j];
}
}
}
return max;
}
int main()
{
printf("%d\n", f("abcdkkk", "baabcdadabc"));
printf("%d\n", f("aaakkkabababa", "baabababcdadabc"));
printf("%d\n", f("abccbaacbcca", "ccccbbbbbaaaa"));
printf("%d\n", f("abcd", "xyz"));
printf("%d\n", f("ab", "ab"));
return 0;
}
2017 年 省赛 正则问题
分析
有同学可能没读懂题意,我在这里举个例子应该就能看懂了
((xx|xxx)x|(x|xx))xx
xx | xxx => 或上的结果要么是 xx 要么就是 xxx 在这里为了保证字符串最长 我们留下了 xxx
(xxx)x就是直接连接为 xxxx
然后(x|xx) 同理 我们留下了 xx
那么现在就是(xxxx | xx) => xxxx
(xxxx)xx => xxxxxx
因此答案为 6
解法: 将正则表达式构建成一颗树,用 dfs 遍历求解即可
Java
import java.util.Scanner;
// 1:无需package
// 2: 类名必须Main, 不可修改
public class Main
{
static String s;
static int k = 0;
public static void main(String[] args)
{
Scanner in = new Scanner(System.in);
s = in.next();
in.close();
System.out.println(dfs());
}
public static int dfs()//计算(.....)中的值
{
int res = 0;
while(k < s.length())
{
if(s.charAt(k) == ')') return res;//遇到 ) 就说明 () 内的已经计算完毕 直接返回即可
else if(s.charAt(k) == '(')
{
k ++;//跳过 (
res += dfs();
k ++;// 跳过 )
}
else if(s.charAt(k) == '|')
{
k ++;
res = Math.max(res,dfs());// 左右取大
}
else
{
k ++;//跳过 x
res ++;//字符串长度增加
}
}
return res;
}
}
C ++
#include<bits/stdc++.h>
using namespace std;
string str;
int k;
int dfs()
{
int res = 0;
while(k < str.size())
{
if(str[k] == ')') break;
if(str[k] == '(')
{
k ++;
res += dfs();
k ++;
}
else if(str[k] == '|')
{
k ++;
res = max(res,dfs());
}
else
{
k ++;
res += 1;
}
}
return res;
}
int main()
{
cin >> str;
cout << dfs() << endl;
return 0;
}
2017 年 省赛 包子凑数
分析
由裴蜀定理可知:若是整数,且 ,那么对于任意的整数都一定是的倍数,特别地,一定存在整数,使 成立,那么倘若 必然会有无数个数字不能被表示。
那么INF 的情况我们就已经讨论出来了
再来看看非 INF 的情况,即
若 互质,那么任何大于 的数都可以表示为
因此当 时,大于 的数都可以被表示,我们只需要对 以内的数判断一下就好了
状态转移
完全背包
表示 数 能被 种方式表示 若 那么就是不能被表示的
f[0] = 1;
for(int i = 1;i <= n;i ++)//枚举 i 种不同的笼子
for(int j = 1;j <= 10010;j ++)//枚举 1 ~ 10000 这些数
{
if(j - a[i] >= 0 && f[j - a[i]] >= 1) f[j] ++;//倘若 j-a[i] 可以被表示 那么j一定可以被表示
}
Java
import java.util.Scanner;
public class Main
{
static int f[] = new int[10100];
static int a[] = new int[10100];
public static int gcd(int a,int b)
{
if(b != 0) return gcd(b,a % b);
else return a;
}
public static void main(String args[])
{
Scanner in = new Scanner(System.in);
int n = in.nextInt();
for(int i = 1;i <= n;i ++)
a[i] = in.nextInt();
in.close();
int tmp = a[1];
for(int i = 2;i <= n;i ++)
tmp = gcd(tmp,a[i]);
if(tmp != 1)
{
System.out.printf("INF");
return ;
}
else
{
f[0] = 1;
for(int i = 1;i <= n;i ++)
for(int j = 0;j <= 10010;j ++)
if((j - a[i] >= 0) && (f[j - a[i]] != 0)) f[j] ++;
int cnt = 0;
for(int i = 1;i <= 10010;i ++) if(f[i] == 0) cnt ++;
System.out.printf("%d\n",cnt);
}
return ;
}
}
C ++
#include<bits/stdc++.h>
using namespace std;
const int N = 20000;
int f[N],a[N];
int gcd(int a,int b)
{
return b ? gcd(b,a % b) : a;
}
int main()
{
int n; cin >> n;
for(int i = 1;i <= n;i ++) cin >> a[i];
int tmp = a[1];
for(int i = 2;i <= n;i ++)
{
tmp = gcd(tmp,a[i]);
}
//cout << tmp << endl;
if(tmp != 1)
{
puts("INF");
return 0;
}
else
{
f[0] = 1;
for(int i = 1;i <= n;i ++)
for(int j = 1;j <= 10010;j ++)
{
if(j - a[i] >= 0 && f[j - a[i]] >= 1) f[j] ++;
}
}
int cnt = 0;
for(int i = 1;i <= 10010;i ++)
{
if(f[i] == 0) cnt ++;
}
cout << cnt << endl;
return 0;
}
2018 年 省赛 填空题 分数
答案: 1048575 / 524288
等比数列求和即可,没啥可写的
2018 年 省赛 填空题 星期一
答案: 5217
以今天是2022/1/25日 星期二为例求解此题
找到最近的星期一 2022/1/24 日
计算1901年1月1日 至 2022年1月24日 有多少天,求得是 44219 天,
那么再计算1901年1月1日 至2000年12月31日有多少天,求得是 36525 天
那么只要从第一天开始枚举是否和 44219对 7 同余就行了
for(int i = 1;i <= 36525;i ++)
{
if(i % 7 == 44219 % 7) res ++;
}
2018 年 省赛 填空题 乘积尾零
要想凑出一个 就需要一个因子 和因子 那么就对所有的数枚举 的因子个数,输出较小的那个就行了,因为要两个搭配才能出
#include<bits/stdc++.h>
using namespace std;
int main()
{
int er = 0,wu = 0;
for(int i = 1;i <= 10;i ++)
for(int j = 1;j <= 10;j ++)
{
int x;
cin >> x;
while(x % 2 == 0 || x % 5 == 0)
{
if(x % 2 == 0)
{
x /= 2;
er ++;
}
else if(x % 5 == 0)
{
x /= 5;
wu ++;
}
}
}
cout << min(er,wu) << endl;
return 0;
}
2018 年 省赛 填空题 第几个幸运数字
答案: 1905
算法分析
暴力枚举仅有 这些因子的数,再利用 set 去重计数 即可
AC code
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
set<LL> s;
const LL N = 59084709587505;
LL qmi(LL a, LL k)
{
LL res = 1;
while (k)
{
if (k & 1) res = (LL)res * a;
a = (LL)a * a;
k >>= 1;
}
return res;
}
int main()
{
LL num;
for(int i = 0;i < 50;i ++)
for(int j = 0;j < 50;j ++)
for(int k = 0;k < 50;k ++)
{
num = qmi(3,i) * qmi(5,j) * qmi(7,k);
if(num > 1 && num <= N) s.insert(num);
}
cout << s.size() << endl;
return 0;
}
2018 年 省赛 代码填空题 打印图形
题目链接
答案: n = size / 3
AC code
#include <stdio.h>
#include <stdlib.h>
void show(char* buf, int w){
int i,j;
for(i=0; i<w; i++){
for(j=0; j<w; j++){
printf("%c", buf[i*w+j]==0? ' ' : 'o');
}
printf("\n");
}
}
void draw(char* buf, int w, int x, int y, int size)// 9 4 4 9
{
if(size==1){
buf[y*w+x] = 1;
return;
}
//w => 每一行有多少个元素
//y => 当前在第几行
//x => 当前在第几列
//size = 9
//9 4 4 n
//9 4-n 4 n
int n = size / 3 ; //填空
draw(buf, w, x, y, n);
draw(buf, w, x-n, y ,n);
draw(buf, w, x+n, y ,n);
draw(buf, w, x, y-n ,n);
draw(buf, w, x, y+n ,n);
}
int main()
{
int N ;
scanf("%d",&N);
int t = 1;
int i;
for(i=0; i<N; i++) t *= 3;
char* buf = (char*)malloc(t*t);
for(i=0; i<t*t; i++) buf[i] = 0;
draw(buf, t, t/2, t/2, t);//draw(buf,9,4,4,9)
show(buf, t);
free(buf);
return 0;
}
2018 年 省赛 航班时间
算法分析
字符串大模拟,注意字符串读入的一些操作
C ++
#include<bits/stdc++.h>
using namespace std;
int time()
{
char s[100];
cin.getline(s,30);
int h1,m1,s1,h2,m2,s2,k = 0;//注意 k 的初始化
if(strlen(s) <= 17) sscanf(s,"%d:%d:%d %d:%d:%d",&h1,&m1,&s1,&h2,&m2,&s2);
else sscanf(s,"%d:%d:%d %d:%d:%d (+%d)",&h1,&m1,&s1,&h2,&m2,&s2,&k);
//printf("%d:%d:%d %d:%d:%d\n",h1,m1,s1,h2,m2,s2);
return ((k * 24 + h2 - h1) * 3600 + (m2 - m1) * 60 + (s2 - s1));
}
void solve()
{
int t1 = time();
int t2 = time();
int res = (t1 + t2) / 2;
//printf("t1 = %d t2 = %d",t1,t2);
int h = res / 3600;
int m = res % 3600 / 60;
int s = res % 3600 % 60;
printf("%02d:%02d:%02d\n",h,m,s);
}
int main()
{
int T;
cin >> T;
getchar();//注意把回车给吃了
while(T --)
solve();
return 0;
}