P8689 [蓝桥杯 2019 国 A] 填空问题 题解
T1 三升序列
思路
这道题需要仔细观察题意,题目中说判断的是子序列个数而不是子串个数。
暴力
考虑枚举以下情况:
- 左右
- 上下
- 左上-右下
- 右上-左下
需要注意的是:
当三个字母是从左下到右上排列时,从左向右看和从上向下看是不同的顺序。
直接模拟即可。
优化
用搜索中的方向数组,可以使代码长度更短。
代码
#include <bits/stdc++.h>
using namespace std;
string mat[30] = {
"VLPWJVVNNZSWFGHSFRBCOIJTPYNEURPIGKQGPSXUGNELGRVZAG",
"SDLLOVGRTWEYZKKXNKIRWGZWXWRHKXFASATDWZAPZRNHTNNGQF",
"ZGUGXVQDQAEAHOQEADMWWXFBXECKAVIGPTKTTQFWSWPKRPSMGA",
"BDGMGYHAOPPRRHKYZCMFZEDELCALTBSWNTAODXYVHQNDASUFRL",
"YVYWQZUTEPFSFXLTZBMBQETXGXFUEBHGMJKBPNIHMYOELYZIKH",
"ZYZHSLTCGNANNXTUJGBYKUOJMGOGRDPKEUGVHNZJZHDUNRERBU",
"XFPTZKTPVQPJEMBHNTUBSMIYEGXNWQSBZMHMDRZZMJPZQTCWLR",
"ZNXOKBITTPSHEXWHZXFLWEMPZTBVNKNYSHCIQRIKQHFRAYWOPG",
"MHJKFYYBQSDPOVJICWWGGCOZSBGLSOXOFDAADZYEOBKDDTMQPA",
"VIDPIGELBYMEVQLASLQRUKMXSEWGHRSFVXOMHSJWWXHIBCGVIF",
"GWRFRFLHAMYWYZOIQODBIHHRIIMWJWJGYPFAHZZWJKRGOISUJC",
"EKQKKPNEYCBWOQHTYFHHQZRLFNDOVXTWASSQWXKBIVTKTUIASK",
"PEKNJFIVBKOZUEPPHIWLUBFUDWPIDRJKAZVJKPBRHCRMGNMFWW",
"CGZAXHXPDELTACGUWBXWNNZNDQYYCIQRJCULIEBQBLLMJEUSZP",
"RWHHQMBIJWTQPUFNAESPZHAQARNIDUCRYQAZMNVRVZUJOZUDGS",
"PFGAYBDEECHUXFUZIKAXYDFWJNSAOPJYWUIEJSCORRBVQHCHMR",
"JNVIPVEMQSHCCAXMWEFSYIGFPIXNIDXOTXTNBCHSHUZGKXFECL",
"YZBAIIOTWLREPZISBGJLQDALKZUKEQMKLDIPXJEPENEIPWFDLP",
"HBQKWJFLSEXVILKYPNSWUZLDCRTAYUUPEITQJEITZRQMMAQNLN",
"DQDJGOWMBFKAIGWEAJOISPFPLULIWVVALLIIHBGEZLGRHRCKGF",
"LXYPCVPNUKSWCCGXEYTEBAWRLWDWNHHNNNWQNIIBUCGUJYMRYW",
"CZDKISKUSBPFHVGSAVJBDMNPSDKFRXVVPLVAQUGVUJEXSZFGFQ",
"IYIJGISUANRAXTGQLAVFMQTICKQAHLEBGHAVOVVPEXIMLFWIYI",
"ZIIFSOPCMAWCBPKWZBUQPQLGSNIBFADUUJJHPAIUVVNWNWKDZB",
"HGTEEIISFGIUEUOWXVTPJDVACYQYFQUCXOXOSSMXLZDQESHXKP",
"FEBZHJAGIFGXSMRDKGONGELOALLSYDVILRWAPXXBPOOSWZNEAS",
"VJGMAOFLGYIFLJTEKDNIWHJAABCASFMAKIENSYIZZSLRSUIPCJ",
"BMQGMPDRCPGWKTPLOTAINXZAAJWCPUJHPOUYWNWHZAKCDMZDSR",
"RRARTVHZYYCEDXJQNQAINQVDJCZCZLCQWQQIKUYMYMOVMNCBVY",
"ABTCRRUXVGYLZILFLOFYVWFFBZNFWDZOADRDCLIRFKBFBHMAXX"
};
int cnt, dc1[79], dp1[79][30][2], dc2[79], dp2[79][30][2];
int main() {
for (int i = 0; i < 30; i++) {
for (int j1 = 0; j1 < 48; j1++) {
for (int j2 = j1 + 1; j2 < 49; j2++) {
for (int j3 = j2 + 1; j3 < 50; j3++) {
cnt+=(mat[i][j1] < mat[i][j2] && mat[i][j2] < mat[i][j3]);
}
}
}
}
for (int j = 0; j < 50; j++) {
for (int i1 = 0; i1 < 28; i1++) {
for (int i2 = i1 + 1; i2 < 29; i2++) {
for (int i3 = i2 + 1; i3 < 30; i3++) {
cnt+=(mat[i1][j] < mat[i2][j] && mat[i2][j] < mat[i3][j]);
}
}
}
}
for (int i = 0; i < 30; i++) {
for (int j = 0; j < 50; j++) {
int idx = i - j + 49;
dp1[idx][dc1[idx]][0] = i, dp1[idx][dc1[idx]][1] = j, dc1[idx]++;
}
}
for (int d = 0; d < 79; d++) {
int n = dc1[d];
if (n < 3) {
continue;
}
for (int a = 0; a < n - 1; a++) {
for (int b = 0; b < n - 1 - a; b++) {
if (dp1[d][b][1] > dp1[d][b + 1][1]) {
swap(dp1[d][b][0], dp1[d][b + 1][0]), swap(dp1[d][b][1], dp1[d][b + 1][1]);
}
}
}
for (int a = 0; a < n - 2; a++) {
for (int b = a + 1; b < n - 1; b++) {
for (int c = b + 1; c < n; c++) {
char c1 = mat[dp1[d][a][0]][dp1[d][a][1]], c2 = mat[dp1[d][b][0]][dp1[d][b][1]], c3 = mat[dp1[d][c][0]][dp1[d][c][1]];
cnt+=(c1 < c2 && c2 < c3);
}
}
}
}
for (int i = 0; i < 30; i++) {
for (int j = 0; j < 50; j++) {
int idx = i + j;
dp2[idx][dc2[idx]][0] = i, dp2[idx][dc2[idx]][1] = j, dc2[idx]++;
}
}
for (int d = 0; d < 79; d++) {
int n = dc2[d];
if (n < 3) {
continue;
}
for (int a = 0; a < n - 2; a++) {
for (int b = a + 1; b < n - 1; b++) {
for (int c = b + 1; c < n; c++) {
int p1r = dp2[d][a][0],p1c = dp2[d][a][1], p2r = dp2[d][b][0],p2c = dp2[d][b][1],p3r = dp2[d][c][0], p3c = dp2[d][c][1];
char ch1 = mat[p1r][p1c],ch2 = mat[p2r][p2c],ch3 = mat[p3r][p3c];
int po[3][3] = {{p1r, p1c, ch1},{p2r, p2c, ch2},{p3r, p3c, ch3}};
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2 - i; j++) {
if (po[j][1] > po[j + 1][1]) {
for (int k = 0; k < 3; k++) {
swap(po[j][k], po[j + 1][k]);
}
}
}
}
bool coi = (po[0][2] < po[1][2] && po[1][2] < po[2][2]);
po[0][0] = p1r, po[0][1] = p1c, po[0][2] = ch1, po[1][0] = p2r, po[1][1] = p2c, po[1][2] = ch2, po[2][0] = p3r, po[2][1] = p3c, po[2][2] = ch3;
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2 - i; j++) {
if (po[j][0] > po[j + 1][0]) {
for (int k = 0; k < 3; k++) {
swap(po[j][k], po[j + 1][k]);
}
}
}
}
bool roi = (po[0][2] < po[1][2] && po[1][2] < po[2][2]);
cnt += (coi || roi);
}
}
}
}
cout << cnt;
return 0;
}
答案
答案为 \(\tt{180414}\)。
T2 最优旅行
思路
这是个特化的 TSP(旅行商问题),是严格难于哈密顿回路的,那么它同时也是个 NP-hard 问题,我们并不能用多项式时间复杂度来做,所以考虑下面两种做法。
DFS
先固定起点,从北京出发,然后枚举剩余点的排列。
时间复杂度 \(O(n!)\),城市较多根本无法跑完。(宇宙毁灭都跑不完)
但是我们发现对于每一城市的列车有固定的出发时间,所以我们可以按照时间剪枝。
然后如果现在的某一条已经比之前的某一条跑完的劣,那么我们可以直接跳过这条路。
这样子可以大大减少搜索时间。
状压 DP(Held-Karp算法)
要到达状态 \((S, j)\),必须从某个状态 \((S-\{j\}, i)\) 转移过来,状态转移方程如下:
最终答案由于要回到北京,所以如下:
时间复杂度 \(O(n^2 2^n)\)。
答案
答案为 \(\tt{47373}\)。
T3 骰子制造
思路
把 \(1\) 到 \(6\) 分配到 \(6\) 个面上,有 \(6!\) 种方案。
而我们考虑把这些数分成两类,发现 \(1,4,5\) 并不会影响旋转结果,所以我们只考虑 \(2,3,6\)。
那么我们这三个数有 \(2\) 个朝向的选择(\(1,3\) 个不会有不同结果),共有 \(3\) 个这样的面,那么总共 \(2^3=8\) 种可能性。
总方案就是 \(6! \times 8 = 5760\)。
考虑一共有 \(24\) 种旋转,那么总共就可以造出 \(\frac{5760}{24} = 240\) 个骰子。
Tips
查阅资料得:若考虑骰子的旋转群,则需要用伯恩赛德引理得出结果。
答案
答案为 \(\tt{240}\)。
T4 序列求和
思路
有一个需要注意的点是:
含有 \(t\) 个约数的整数。
意思并不是恰好有 \(t\) 个约数,而是约数个数 \(\ge t\)。


暴力思路很简单,考虑枚举每一个数,然后求出其约数个数,由于是从小到大枚举所以这里保证是最小的。
代码
#include <bits/stdc++.h>
using namespace std;
int sum;
int main() {
for (int i = 1, x = 1; i <= 60; i++, x = 1) {
for (int cnt = 0; cnt < i; x++) {
cnt = 0;
for (int j = 1; j <= x; j++) {
cnt += (!(x % j));
}
}
sum += (--x);
}
cout << sum;
return 0;
}
答案
答案是 \(\tt{101449}\)。
T5 无方集合
思路
考虑把这一个问题看成一个图论问题,是求 最大团/最大独立集 的 NP 问题,考虑剪枝。
- 如果当前数量和剩余能选数字之和小于等于最大数量,直接返回。
- 去除掉原先就有的完全平方数,然后进行类似搜索的标记数组,再回溯求解。
答案
答案为 \(\tt{36}\)。
答案/代码
#include<iostream>
using namespace std;
int main() {
string ans [] = {
"180414", // 双引号中替换为 A 题的答案
"47373", // 双引号中替换为 B 题的答案
"240", // 双引号中替换为 C 题的答案
"101449", // 双引号中替换为 D 题的答案
"36", // 双引号中替换为 E 题的答案
};
char T;
cin >> T;
cout << ans[T - 'A'] << endl;
return 0;
}
后记
为什么蓝桥杯那么多 NP 问题啊?(恼)

浙公网安备 33010602011771号