写在前面,这届蓝桥杯,考了2道贪心,1道bfs,1道数论,剩下的都是模拟题,除了最后一题之外,其他题还是都很简单的
试题 A: 组队
题解: 这个题其实可以手推,编程实现的话就是dfs暴力枚举所有情况,最后求出评分最大即可。
C++ 代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 25;
int team[N][N];
bool st[N];
int res = 0;
void dfs(int u,int sum)
{
if(u == 6)
{
res = max(res, sum);
return;
}
for(int i = 0; i < 20; i ++ )
{
if(!st[i])
{
st[i] = true;
dfs(u + 1, sum + team[i][u]);
st[i] = false;
}
}
}
int main()
{
freopen("team.txt", "r", stdin);
for(int i = 0; i < 20; i ++ )
for(int j = 0; j < 6; j ++ )
cin >> team[i][j];
dfs(1, 0);
cout << res << endl;
return 0;
}
//490
试题 B:年号字串
题解:
直接考虑成26进制就可以,注意最后要reverse
一下
#include <iostream>
#include <algorithm>
using namespace std;
int main()
{
string str = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
string res;
int x = 2019;
while(x)
{
int t = x % 26;
x /= 26;
res += str[t - 1];
}
reverse(res.begin(), res.end());
cout << res << endl;
return 0;
}
//BYQ
试题 C:数列求值
题解: 很简单的一个dp操作,但是要注意的是题目要求输出后四位,所以需要%10000
C++ 代码:
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 20200000;
long long dp[N];
void dfs()
{
for(int i = 3; i <= 20190324; i ++ )
dp[i] = (dp[i - 1] + dp[i - 2] + dp[i - 3]) % 10000;
}
int main()
{
dp[0] = 1;
dp[2] = 1;
dp[1] = 1;
dfs();
cout << dp[20190323] << endl;
return 0;
}
// 4659
试题 D:数的分解
题解: 直接模拟就可以,注意i,j,k的顺序,我们直接人为规定i < j < k
来确保我们的答案的唯一性
C++ 代码:
#include <iostream>
#include <algorithm>
using namespace std;
bool check(int x)
{
while(x)
{
int t = x % 10;
x /= 10;
if(t == 2 || t == 4) return false;
}
return true;
}
int main()
{
int res = 0;
for(int i = 1; i < 2019; i ++ )
if(check(i))
{
for(int j = i + 1; j < 2019; j ++ )
if(check(j))
{
int k = 2019 - i - j;
if(check(k) && k > j) res ++;
}
}
cout << res << endl;
return 0;
}
// 40785
试题 E:迷宫
01010101001011001001010110010110100100001000101010
00001000100000101010010000100000001001100110100101
01111011010010001000001101001011100011000000010000
01000000001010100011010000101000001010101011001011
00011111000000101000010010100010100000101100000000
11001000110101000010101100011010011010101011110111
00011011010101001001001010000001000101001110000000
10100000101000100110101010111110011000010000111010
00111000001010100001100010000001000101001100001001
11000110100001110010001001010101010101010001101000
00010000100100000101001010101110100010101010000101
11100100101001001000010000010101010100100100010100
00000010000000101011001111010001100000101010100011
10101010011100001000011000010110011110110100001000
10101010100001101010100101000010100000111011101001
10000000101100010000101100101101001011100000000100
10101001000000010100100001000100000100011110101001
00101001010101101001010100011010101101110000110101
11001010000100001100000010100101000001000111000010
00001000110000110101101000000100101001001000011101
10100101000101000000001110110010110101101010100001
00101000010000110101010000100010001001000100010101
10100001000110010001000010101001010101011111010010
00000100101000000110010100101001000001000000000010
11010000001001110111001001000011101001011011101000
00000110100010001000100000001000011101000000110011
10101000101000100010001111100010101001010000001000
10000010100101001010110000000100101010001011101000
00111100001000010000000110111000000001000000001011
10000001100111010111010001000110111010101101111000
题解 bfs + 路径记忆
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
const int n = 30, m = 50;
int g[n][m];
struct Point
{
int x, y;
string path;
}P;
char op[4] = {'D', 'L', 'R', 'U'};
int dx[4] = {1, 0, 0, -1}, dy[4] = {0, -1, 1, 0};
void bfs()
{
queue<Point> q;
Point p({0,0});
q.push(p);
p.path = "";
while(q.size())
{
Point pt = q.front();
q.pop();
if(pt.x == 29 && pt.y == 49)
{
cout << pt.path << endl;
return;
}
for(int i = 0; i < 4; i ++ )
{
int tx = pt.x + dx[i], ty = pt.y + dy[i];
if(tx >= 0 && tx < 30 && ty >= 0 && ty < 50 && !g[tx][ty])
{
g[tx][ty] = 1;
Point pp({tx, ty});
pp.path = pt.path + op[i];
q.push(pp);
}
}
}
}
int main()
{
freopen("migong.txt", "r", stdin);
string t;
for(int i = 0; i < n; i ++ )
{
cin >> t;
for(int j = 0; j < m; j ++ )
{
g[i][j] = t[j] - '0';
}
}
bfs();
return 0;
}
答案:
DDDDRRURRRRRRDRRRRDDDLDDRDDDDDDDDDDDDRDDRRRURRUURRDDDDRDRRRRRRDRRURRDDDRRRRUURUUUUUUULULLUUUURRRRUULLLUUUULLUUULUURRURRURURRRDDRRRRRDDRRDDLLLDDRRDDRDDLDDDLLDDLLLDLDDDLDDRRRRRRRRRDDDDDDRR
试题 F:特别数的和
题解: 模拟就好了
C++ 代码:
#include <iostream>
#include <algorithm>
using namespace std;
bool check(int x)
{
while(x)
{
int t = x % 10;
if(t == 0 || t == 1 || t == 2 || t == 9) return true;
x /= 10;
}
return false;
}
int main()
{
int n;
cin >> n;
long long res = 0;
for(int i = 1; i <= n; i ++ )
{
if(check(i)) res += i;
}
cout << res << endl;
return 0;
}
试题 G:完全二叉树的权值
题解:
完全二叉树的权值
双指针
每一段的长度是
i
+
2
(
d
−
1
)
i+2^{(d-1)}
i+2(d−1), 并且j不能越界
具有n个结点的完全二叉树的深度
l
o
g
2
k
+
1
log_{2}k +1
log2k+1
C++ 代码:
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 100010;
int n;
int tree[N];
int main()
{
scanf("%d", &n);
for(int i = 1; i <= n; i ++ ) scanf("%d", &tree[i]);
long long maxx = -1e6;
int depth = 0;
for(int i = 1, d = 1; i <= n; i *= 2, d ++ )
{
long long t = 0;
for(int j = i; j < i + (1 << (d - 1)) && j <= n; j ++ )
t += tree[j];
if(t > maxx)
{
maxx = t;
depth = d;
}
}
cout << depth << endl;
return 0;
}
试题 H:等差数列
题解:
每一项与第一项的差一定是d的倍数
当d != 0 时, (a末 - a初) / d + 1 ---- 让公差d最大即可
当d == 0 时,答案为 n
c++ 代码:
#include <iostream>
#include <algorithm>
#include <climits>
using namespace std;
const int N = 100010;
int n;
int a[N];
int main()
{
cin >> n;
for(int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
sort(a, a + n);
int mgcd = 0;
for(int i = 1; i < n; i ++ )
{
mgcd = __gcd(mgcd, a[i] - a[0]);
}
if(mgcd == 0)
{
cout << n << endl;
return 0;
}
cout << (a[n - 1] - a[0]) / mgcd + 1 << endl;
return 0;
}
试题 I:后缀表达式
题解:
给题意翻译翻译,其实就是有n个加号,m个减号,n+m+1个数,可以加括号,问组成表达式的最大值。
特殊情况:m=0,直接输出和
一般情况:把所有数排个序,最大的拿出来,放首项,把最小的数拿出来,给他一个减号,再套一个括号,那么现在还未完成的表达式长这样:
可以发现,现在如果我想加一个数的话,给它一个加号,放在括号外面,也可以给它一个减号,放在括号里面;减一个数同理。换句话说,只要用一个减号,一个最大值,一个最小值,其他数我想加就加,想减就减。那么为了使结果最大,我加上正数,减去负数,就是直接加上所有剩下数的绝对值,那么就解决了。
C++ 代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N = 200010;
typedef long long LL;
int n, m;
int a[N];
int main()
{
scanf("%d%d", &n, &m);
int k = n + m + 1;
for(int i = 0; i < k; i ++ ) scanf("%d", &a[i]);
sort(a, a + k);
LL res = 0;
if(m <= 0)
for(int i = 0; i < k; i ++ ) res += a[i];
else
{
res = a[k - 1] - a[0];
for(int i = 1; i < k - 1; i ++ ) res += abs(a[i]);
}
cout << res << endl;
return 0;
}
试题 J:灵能传输
题解:
搞了好久这题,终于明白了,过程分的有点多,而且每一个过程都不是很容易想到,所以会觉得听完y总讲的后感觉根本没听懂,我是也是前后听了好几遍才明白的,然后把思路大致整理一下
搞明白这一题就一定要时刻铭记我们的最终目标是什么。
根据题意我们的最终目标就是使得每个战士的灵能的不稳定度最小,就是经过变化后,序列里的所有数离0最大值最小
可以考虑我们所要求的序列是
a
i
a_i
ai 那么可以考虑用前缀和数组来表示
a
i
a_i
ai 就是
s
i
−
s
i
−
1
s_i - s_{i-1}
si−si−1
那么有了这个基础我们来看题目要求的数据变化,形式化来讲就是
a
i
−
1
+
=
a
i
,
a
i
+
1
+
=
a
i
,
a
i
−
=
2
⋅
a
i
a_{i−1} += a_i , a_{i+1} += a_i, a_i −= 2·a_i
ai−1+=ai,ai+1+=ai,ai−=2⋅ai
考虑
a
a
a 序列的变化对于
s
s
s 数组的影响,可以看出加了2个
a
i
a_i
ai 同时减了2个
a
i
a_i
ai那么对于
s
i
+
1
s_{i + 1}
si+1 以及后面
s
i
+
k
s_{i+k}
si+k来说都没有任何影响,看
s
i
−
1
s_{i - 1}
si−1 可以看出多加了一个
a
i
a_i
ai 所以
s
i
−
1
s_{i-1}
si−1变为
s
i
s_i
si 看
s
i
s_i
si
可以看出
a
i
−
1
a_{i-1}
ai−1 加了一个
a
i
a_i
ai ,
a
i
a_i
ai减了两个
a
i
a_i
ai所以
s
i
s_i
si 总共减了一个
a
i
a_i
ai变为
s
i
−
1
s_{i-1}
si−1
过程①总结一下:
-
考虑用前缀和来表示 a i a_i ai ,找出了经题目要求变化 s 前 s_{前} s前与 s 后 s_{后} s后的区别 这也是最难想到的一步
-
这意味着除了
s[0]
和s[n]
以外$ 1 - n $ 的任何s[i]
可以进行相互交互从而得到一个有序的序列,而a[i]=s[i]-s[i-1]
,也就意味着可以通过交换s[i]
的方式得到灵能传输后最终结果 -
根据题意我们要求的是 a i a_i ai 距 0 的最大值最小是多少, 转化为 s i − s i − 1 s_i - s_{i - 1} si−si−1 最大值最小是多少
这样就引出了我们的我第二步,什么样的前缀和序列使得max(s[i] - s[i-1])
最小,利用贪心的思路知:有序的前缀和序列使得所求最小
下面是这一思路的证明:一个序列一定存在一个最大值和一个最小值,如果不是有序序列的话,即最大值和最小值不在两边的时候,就会出现曲线,如图:
这样就会存在最大值和最小值刚好相邻,但是求其差值回为最大。其实分析到这里这个题的大体就结束了,但是狗就狗在
s0 和 sn 不能计算到排序中,因为a0 和 an 在两边导致s0 和 sn的 位置固定,我们无法修改这两个数的位置,那么就使得我们最终得到的si序列不是单调的
这就引出了我们的第③步
在一直s0和sn的前提下,怎样使得我们得到的序列满足我们的要求呢,要求是什么,这一点很重要,千万别忘,否则就很难理解这一步,回头翻翻开头和第一步。
第三步:
在s0和sn无法改变在序列中位置的前提下,怎样满足我们要求呢:
下面给出两种一般情况
这里假设s0 是小于sn的(如果是大于,swap一下就是一样的)那么明显第一个图的在y轴视角的叠次数更多,而第二个图的重叠次数更少,重叠次数更小说明我们所求的s[i] - s[i-1]会更大,这样才能保证是求得最大值,如果重叠次数越多那就说明压到y轴时越稠密,那么就是最大值了
分析到这里就完成了第三步,还剩下最后一步,经过我的理解,我发现了 很透彻 最后一步的讲解,为什么要跳着取而不是顺序取
完事完事,真滴难
c++代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
using namespace std;
const int N = 300010;
typedef long long LL;
int n;
LL a[N], s[N];
bool st[N];
int main()
{
int t;
scanf("%d", &t);
while(t -- )
{
scanf("%d", &n);
s[0] = 0;
for(int i = 1; i <= n; i ++ )
{
scanf("%lld", &a[i]);
s[i] = s[i - 1] + a[i];
}
LL s0 = s[0], sn = s[n];
if(s0 > sn) swap(s0, sn);
sort(s, s + n + 1);
for(int i = 0; i <= n; i ++ )
if(s[i] == s0)
{
s0 = i;
break;
}
for(int i = n; i >= 0; i -- )
if(s[i] == sn)
{
sn = i;
break;
}
memset(st, 0,sizeof st);
int l = 0 ,r = n;
for(int i = s0; i >= 0; i -= 2)
{
a[l ++ ] = s[i];
st[i] = true;
}
for(int i = sn; i <= n; i += 2)
{
a[r -- ] = s[i];
st[i] = true;
}
for(int i = 0; i <= n; i ++ )
if(!st[i]) a[l ++ ] = s[i];
LL res = 0;
for(int i = 1; i <= n; i ++ ) res = max(res, abs(a[i] - a[i - 1]));
printf("%lld\n", res);
}
return 0;
}
总结:自己太菜了