第七届蓝桥杯

前言

(不会吧不会吧不会吧,不会真有大学生到快到四月都没开学吧)
在这里插入图片描述
乌丸千岁(💊千岁)

网友年龄

在这里插入图片描述
直接循环嵌套穷举就好了,没啥好说的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <bits/stdc++.h>
using namespace std;
// 传送门:https://www.lanqiao.cn/courses/2786/learning/?id=67636
// 循环嵌套
int main()
{
int ans = 0;
for (int i = 0; i <= 9; i++)
for (int j = 0; j <= 9; j++)
{
if (((i * 10 + j) - (j * 10 + i)) == 27)
ans++;
}
cout << ans;
return 0;
}

生日蜡烛


没什么好办法,从0开始枚举开始过生日的可能情况,依次判断就行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <bits/stdc++.h>
using namespace std;
// https://www.lanqiao.cn/courses/2786/learning/?id=67636
// 枚举!
int judge(int x)
{
int sum = 236;
int cnt = 0;
int year = x;
while (sum)
{
if (sum < 0)
return 0;
sum -= year;
year--;
}
return year + 1;
}
int main()
{
for (int i = 20; i < 100; i++)
{
if (judge(i))
cout << judge(i);
}
return 0;
}

方格填数

在这里插入图片描述
在这里插入图片描述
对角也算相邻!!!dfs模板题,正常思路是先在之前的递归中将数字填入,最后在最后一次循环进行判断即可。
我当时做麻烦了,以边的形式存图,其实用连接矩阵更好一点。
不过用边存图的话dfs好写一点
总之各有千秋吧,能做出来就行
我的思路大体是,在dfs中间枚举可能情况,边搜边判断,如果有不符合的情况直接筛掉,到最后能构造完的就是正解。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <bits/stdc++.h>
using namespace std;
// https://www.lanqiao.cn/courses/2786/learning/?id=67636
// 深搜+回溯
#define INF 0x3f3f3f3f
int ans;
bool visit[10];
int pos[10];
vector<int> edge[10];
void start()
{
edge[0].push_back(1), edge[0].push_back(4), edge[0].push_back(3), edge[0].push_back(5);
edge[1].push_back(0), edge[1].push_back(5), edge[1].push_back(2), edge[1].push_back(4), edge[1].push_back(6);
edge[2].push_back(1), edge[2].push_back(6), edge[2].push_back(5);
edge[3].push_back(4), edge[3].push_back(7), edge[3].push_back(8), edge[3].push_back(0);
edge[4].push_back(3), edge[4].push_back(8), edge[4].push_back(5), edge[4].push_back(0), edge[4].push_back(9), edge[4].push_back(7), edge[4].push_back(1);
edge[5].push_back(1), edge[5].push_back(4), edge[5].push_back(9), edge[5].push_back(6), edge[5].push_back(0), edge[5].push_back(2), edge[5].push_back(8);
edge[6].push_back(2), edge[6].push_back(5), edge[6].push_back(1), edge[6].push_back(9);
edge[7].push_back(3), edge[7].push_back(8), edge[7].push_back(4);
edge[8].push_back(7), edge[8].push_back(4), edge[8].push_back(9), edge[8].push_back(3), edge[8].push_back(5);
edge[9].push_back(8), edge[9].push_back(5), edge[9].push_back(4), edge[9].push_back(6);
}
bool check(int x, int num)
{
for (int i = 0; i < edge[x].size(); i++)
{
if (abs(num - pos[edge[x][i]]) == 1)
{
return false;
}
}
return true;
}
void dfs(int x)
{
if (x == 10)
{
ans++;
// cout << endl;
return;
}
for (int i = 0; i <= 9; i++)
{
if (visit[i] == 0)
{
if (!check(x, i))
continue;
pos[x] = i;
visit[i] = 1;
// cout << i << " ";
// cout<<i<<" ";
dfs(x + 1);
visit[i] = 0;
pos[x] = -10;
}
}
}
int main()
{
start();
// memset(pos, INF, sizeof(pos));
for (int i = 0; i <= 9; i++)
pos[i] = -10;
dfs(0);
cout << ans;
return 0;
}

二刷换了一个写法,思路差不多,区别是先枚举,最后判断,这样写可能慢了一点,但是更清晰

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <bits/stdc++.h>
using namespace std;
vector<int> edge[11];
int pos[11];
int ans;
bool visit[10];
void init()
{
for(int i=0;i<10;i++)
pos[i]=-199;
edge[0].push_back(1), edge[0].push_back(4), edge[0].push_back(3), edge[0].push_back(5);
edge[1].push_back(0), edge[1].push_back(5), edge[1].push_back(2), edge[1].push_back(4), edge[1].push_back(6);
edge[2].push_back(1), edge[2].push_back(6), edge[2].push_back(5);
edge[3].push_back(4), edge[3].push_back(7), edge[3].push_back(8), edge[3].push_back(0);
edge[4].push_back(3), edge[4].push_back(8), edge[4].push_back(5), edge[4].push_back(0), edge[4].push_back(9), edge[4].push_back(7), edge[4].push_back(1);
edge[5].push_back(1), edge[5].push_back(4), edge[5].push_back(9), edge[5].push_back(6), edge[5].push_back(0), edge[5].push_back(2), edge[5].push_back(8);
edge[6].push_back(2), edge[6].push_back(5), edge[6].push_back(1), edge[6].push_back(9);
edge[7].push_back(3), edge[7].push_back(8), edge[7].push_back(4);
edge[8].push_back(7), edge[8].push_back(4), edge[8].push_back(9), edge[8].push_back(3), edge[8].push_back(5);
edge[9].push_back(8), edge[9].push_back(5), edge[9].push_back(4), edge[9].push_back(6);
}
bool judge(int x) //判断第x格子行不行
{
int l=edge[x].size();
for(int i=0;i<l;i++)
{
int y=edge[x][i];
if(abs(pos[x]-pos[y])==1)
return false;
}
return true;
}
void dfs(int x)
{
if(x==10)
{
for(int i=0;i<9;i++)
{
if(!judge(i))
return;
}
ans++;
}
else
{
for(int i=0;i<10;i++)
{
if(visit[i])
continue;
visit[i]=1;
pos[x]=i;
dfs(x+1);
visit[i]=0;
}
}
}
int main()
{
init();
dfs(0);
cout<<ans;
return 0;
}

快速排序

在这里插入图片描述
在这里插入图片描述在这里插入图片描述
在这里插入图片描述
考察快排的基本思路,学过的应该秒出答案吧 (不好意思我不会现学的) 其实先学先用也可以,题目给了快排的思路,可以照葫芦画瓢

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// 快排解释:https://blog.csdn.net/Mr_xiayijie/article/details/80469293?ops_request_misc=%257B%2522request%255Fid%2522%253A%2522164736128916780357279308%2522%252C%2522scm%2522%253A%252220140713.130102334.pc%255Fall.%2522%257D&request_id=164736128916780357279308&biz_id=0&utm_medium=distribute.pc_search_result.none-task-blog-2~all~first_rank_ecpm_v1~rank_v31_ecpm-4-80469293.142^v2^pc_search_result_cache,143^v4^control&utm_term=%E5%BF%AB%E6%8E%92%E5%8E%9F%E7%90%86&spm=1018.2226.3001.4187
// 快排基本原理
#include <stdio.h>

void swap(int a[], int i, int j)
{
int t = a[i];
a[i] = a[j];
a[j] = t;
}

int partition(int a[], int p, int r)
{
int i = p;
int j = r + 1;
int x = a[p];
while (1)
{
while (i < r && a[++i] < x)
;
while (a[--j] > x)
;
if (i >= j)
break;
swap(a, i, j);
}
swap(a, p, j); //填空在这里
//解释:这个时候a[j]已经比x小了,此时交换可以满足左边比x小,右边比x大
return j;
}

void quicksort(int a[], int p, int r)
{
if (p < r)
{
int q = partition(a, p, r);
quicksort(a, p, q - 1);
quicksort(a, q + 1, r);
}
}

int main()
{
int i;
int a[] = {5, 13, 6, 24, 2, 8, 19, 27, 6, 12, 1, 17};
int N = 12;

quicksort(a, 0, N - 1);

for (i = 0; i < N; i++)
printf("%d ", a[i]);
printf("\n");

return 0;
}

寒假作业

在这里插入图片描述
很有意思的一道dfs题,思路和之前的那道题思路很像,边搜边排除,到最后能搜完的就是正解,思路出来了代码很好写

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <bits/stdc++.h>
using namespace std;
// 传送门:https://www.lanqiao.cn/courses/2786/learning/?id=264529
// dfs+剪枝
int ans;
bool visit[14];
void dfs(int cnt)
{
if (cnt == 1) //算加法
{
for (int i = 1; i <= 13; i++)
{
for (int j = 1; j <= 13 - i; j++)
{
if (visit[i] || visit[j])
continue;
if (i + j > 13)
continue;
if (i == j)
continue;
visit[i] = visit[j] = visit[i + j] = true;
// cout << i << "+" << j << " " << i + j << endl;
dfs(2);
visit[i] = visit[j] = visit[i + j] = false;
}
}
}
if (cnt == 2) // 算减法
{
for (int i = 1; i <= 13; i++)
for (int j = 1; j < i; j++)
{
if (visit[i] || visit[j] || visit[i - j])
continue;
if (j == (i - j))
continue;
visit[i] = visit[j] = visit[i - j] = true;
// cout << i << "-" << j << " " << i - j << endl;
dfs(3);
visit[i] = visit[j] = visit[i - j] = false;
}
}
if (cnt == 3)
{
for (int i = 1; i <= 13; i++)
for (int j = 1; j <= (13 / i); j++)
{
if (visit[i] || visit[j] || visit[i * j])
continue;
if (i == j || i == i * j || j == i * j)
continue;
// cout << i << "*" << j << " " << i * j << endl;
visit[i] = visit[j] = visit[i * j] = true;
dfs(4);
visit[i] = visit[j] = visit[i * j] = false;
}
}
if (cnt == 4)
{
for (int i = 1; i <= 13; i++)
for (int j = 1; j < i; j++)
{
if (i % j != 0)
continue;
if (visit[i] || visit[j] || visit[i / j])
continue;
if (i == j || i == i / j || j == i / j)
continue;
// cout << i << "/" << j << " " << i / j << endl;
ans++;
}
}
}
int main()
{
dfs(1);
cout << ans;
return 0;
}

剪邮票

在这里插入图片描述
在这里插入图片描述
也是dfs题,我当时自己写的时候犯了一个错误,首先讲一下我的思路:首先用dfs生成五个不相同的数字,然后将这5个数字在图上对应的地方标记,然后dfs图看看是否为连通块。但是要注意:假如你使用dfs搜出来的数字组合,你搜出来的序列是有重复的!12345和54321是一种情况! ,当时我第一次跑的时候答案是一万多,后来想了一个多小时,才发现是这个错了,解决方式很简单。每一种情况会被重复5!次,结果除一下5!就行了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#include <bits/stdc++.h>
using namespace std;
// 传送门:https://www.lanqiao.cn/courses/2786/learning/?id=67636
// dfs
// 思路:首先用dfs生成五个不相同的数字,然后将这5个数字在图上对应的地方标记,然后dfs图看看是否为连通块
int pos_x[] = {0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3};
int pos_y[] = {0, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3};
bool visit[13];
bool edge[4][5];
int ans;
int dirx[] = {0, 0, 1, -1};
int diry[] = {1, -1, 0, 0};
bool sum[100000];
void dfs(int x, int y, int &cnt)
{
if (!(x <= 2 && x >= 0 && y <= 3 && y >= 0))
return;
if (edge[x][y] == false)
return;
cnt++;
// cout << "x" << x << " "
// << "y" << y << " ";
edge[x][y] = false;
for (int i = 0; i < 4; i++)
{
int x1 = x + dirx[i];
int y1 = y + diry[i];
dfs(x1, y1, cnt);
}
}
void check()
{
memset(edge, 0, sizeof(edge));
int x1;
int y1;
for (int i = 1; i <= 12; i++)
{
if (visit[i])
{
edge[pos_x[i]][pos_y[i]] = true;
x1 = pos_x[i];
y1 = pos_y[i];
// cout << i << " " << x1 << " " << y1 << endl;
}
}
int cnt = 0;
dfs(x1, y1, cnt);
// cout<<cnt;
// cout << endl;
if (cnt == 5)
{
ans++;
}
}
void select(int cnt)
{
if (cnt > 5)
{
// for (int i = 1; i <= 12; i++)
// {
// if (visit[i] == true)
// cout << i << " ";
// }
// cout << endl;
check();
return;
}
else
{
for (int i = 1; i <= 12; i++)
{
if (visit[i] == false)
{
visit[i] = true;
select(cnt + 1);
visit[i] = false;
}
}
}
}
int main()
{
select(1);
cout << ans / (5 * 4 * 3 * 2 * 1);
return 0;
}

二刷换了一个写法,更清晰一点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <bits/stdc++.h>
using namespace std;
int maze[4][5];
int visit[4][5];
int posx[]={0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4};
int posy[]={0,1,2,3,4,1,2,3,4,1,2,3,4,1,2,3,4};
int dirx[]={0,0,-1,1};
int diry[]={1,-1,0,0};
int ans;
int cnt;
void get(int x,int y)
{
for(int i=0;i<4;i++)
{
int rempx=x+dirx[i];
int rempy=y+diry[i];
if(!(rempx<=3&&rempx>=1&&rempy<=4&&rempy>=1))
continue;
if(maze[rempx][rempy]&&visit[rempx][rempy]==false)
{
visit[rempx][rempy]=true;
get(rempx,rempy);
}
}
}
bool judge()
{
cnt=0;
memset(visit,0,sizeof(visit));
for(int i=1;i<=3;i++)
for(int j=1;j<=4;j++)
{
if(maze[i][j]&&visit[i][j]==false)
{
visit[i][j]=1;
get(i,j);
cnt++;
}
}
if(cnt==1)
return true;
else
return false;
}
void dfs(int num)
{
if(num<=5)
{
for(int i=1;i<=12;i++)
{
int x=posx[i];
int y=posy[i];
if(maze[x][y])
continue;
maze[x][y]=true;
dfs(num+1);
maze[x][y]=false;
}
}
else
{
if(judge())
ans++;
}
}
int main()
{
dfs(1);
cout<<ans/(5*4*3*2*1);
return 0;
}

四平方和

在这里插入图片描述在这里插入图片描述
这道题是真的离谱。。。。我当时一开始写,想了一堆优化方案。。。
我的自己思路是这样的; 首先枚举后两项,将大的数字组合先存起来,然后从头枚举前两个小的,再二分查找满足条件的后两项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <bits/stdc++.h>
using namespace std;
// 传送门:http://lx.lanqiao.cn/problem.page?gpid=T2766
// 二分
long long num;
int cnt = 0;
struct node
{
int c, d;
int sumcd;
} edge[10000000]; //存储c,d
bool cmp(node x, node y)
{
// if (x.d != y.d)
// return x.d > y.d;
if (x.sumcd!=y.sumcd) return x.sumcd < y.sumcd;
if (x.c!=y.c) return x.c<y.c;
return x.d<y.d;
}
bool check(int a, int b, int mid)
{
if (a * a + b * b + edge[mid].sumcd >= num)
return true;
else
return false;
}
void get_ans()
{
for (int i = 0; i*i <=num; i++) //枚举后一项(d)
{
for (int j = i; j*j+i*i <=num; j++) //枚举后一项(c)
//注意循环的写法有讲究:
// i从最大的maxn开始循环,而j从0开始找
{
cnt++;
edge[cnt].c = i;
edge[cnt].d = j;
edge[cnt].sumcd = i * i + j * j;
}
}
sort(edge + 1, edge + cnt + 1, cmp);
for (int i = 0; i*i <= num; i++)
for (int j = i; j*j+i*i <= num; j++)
// 从a最小开始找,因为题目要求找第一个,即a最小
{
int left = 1;
int right = cnt;
int ans = -1;
while (left <= right)
{
int mid = (left + right) >> 1;
if (check(i, j, mid))
{
ans = mid;
right = mid - 1;
}
else
left = mid + 1;
}
if (i * i + j * j + edge[ans].sumcd == num)
{
// cout << i * i + j * j + edge[ans].sumcd << endl;
// cout << num << endl;
cout << i << " " << j << " " << edge[ans].c << " " << edge[ans].d;
return;
}
}
}
int main()
{
ios::sync_with_stdio(false);
cin >> num;
get_ans();
return 0;
}

但是!这道题压根不用这么麻烦!!!!
四重暴力循环会超时,但是可以三维优化一下就可以解决问题!!!
该说不愧是暴力杯啊???真有你的蓝桥杯!!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include <bits/stdc++.h>
using namespace std;
// 传送门:http://lx.lanqiao.cn/problem.page?gpid=T2766
// 暴 力 循 环
// 把四维优化为三维,我giao竟然ac了!蓝桥杯真有你的!不愧是暴力杯
int main()
{
ios::sync_with_stdio(false);
long long num;
cin >> num;
int maxn = sqrt(num);
for (int i = 0; i <= maxn; i++)
for (int j = i; j <= maxn; j++)
for (int z = j; z <= maxn; z++)
{
int remp = sqrt(num - (i * i + j * j + z * z));
if (remp * remp + i * i + j * j + z * z == num)
{
cout << i << " " << j << " " << z << " " << remp;
return 0;
}
}
return 0;
}

另外再加dxy大佬的另一个解法:
鸣谢dxy大佬!!!!

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define pii pair<int,int>
const int mod=1e9+7;
const int INF=0x3f3f3f3f;
const int N=5e6+5;
int a[N],b[N];
int main(){
ios::sync_with_stdio(false);
cin.tie(0),cout.tie(0);
int n;
cin>>n;
memset(a,-1,sizeof a);
for (int i=0;i*i<=n;i++){
for (int j=i;j*j+i*i<=n;j++){
int temp=i*i+j*j;
if (a[temp]!=-1) continue;
a[temp]=i;
b[temp]=j;
}
}
for (int i=0;i*i<=n;i++){
for (int j=i;i*i+j*j<=n;j++){
int temp=n-i*i-j*j;
if (a[temp]!=-1){
cout<<i<<" "<<j<<" "<<a[temp]<<" "<<b[temp]<<"\n";
return 0;
}
}
}
return 0;
}

(我是伞兵)
在这里插入图片描述

密码脱落

在这里插入图片描述
一道区间dp模版题,理解了思路不难
贴一个区间dp模板

1
2
3
4
5
6
7
8
9
10
11
12
13
区间dp模板
memset(dp, 0, sizeof(dp));
//初始dp数组
for (int len = 2; len <= n; len++)
{
//枚举区间长度
for (int i = 1; i + len - 1 < n; ++i)
{ //枚举区间的起点
int j = i + len - 1; //根据起点和长度得出终点
for (int k = i; k <= j; ++k) //枚举最优分割点
//状态转移方程
}
}

下面是全代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
  	#include <bits/stdc++.h>
using namespace std;
// 区间dp
// 传送门:http://lx.lanqiao.cn/problem.page?gpid=T2767
// 其实就是求最长回文区间长度,最终结果就是总长减去最长回文区间
int dp[10000][10000];
int main()
{
string str;
cin >> str;
int len = str.length();
for (int l = 1; l <= len; l++) //枚举区间长度
{
for (int i = 0; i + l - 1 < len; i++) //枚举区间起点
{
int j = i + l - 1; //区间终点
if (l == 1)
{
dp[i][j] = 1; //长度为一的区间回文长度为1
}
else
{
dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]); //一种是选右不选左,另一种是选左不选右
if (str[i] == str[j])
{
dp[i][j] = max(dp[i][j], dp[i + 1][j - 1] + 2);
}
}
}
}
cout << len - dp[0][len - 1];
return 0;
}
/*
区间dp模板
memset(dp, 0, sizeof(dp));
//初始dp数组
for (int len = 2; len <= n; len++)
{
//枚举区间长度
for (int i = 1; i + len - 1 < n; ++i)
{ //枚举区间的起点
int j = i + len - 1; //根据起点和长度得出终点
for (int k = i; k <= j; ++k) //枚举最优分割点
//状态转移方程
}
}

*/

消除尾一

在这里插入图片描述

代码填空,理解题意就好了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <stdio.h>

void f(int x)
{
int i;
for (i = 0; i < 32; i++)
printf("%d", (x >> (31 - i)) & 1);
printf(" ");

x = x & (x + 1);
for (i = 0; i < 32; i++)
printf("%d", (x >> (31 - i)) & 1);
printf("\n");
}

int main()
{
f(103);
f(12);
return 0;
}