P8689 [蓝桥杯 2019 国 A] 填空问题 题解

T1 三升序列

思路

这道题需要仔细观察题意,题目中说判断的是子序列个数而不是子串个数。

暴力

考虑枚举以下情况:

  1. 左右
  2. 上下
  3. 左上-右下
  4. 右上-左下

需要注意的是:

当三个字母是从左下到右上排列时,从左向右看和从上向下看是不同的顺序。

直接模拟即可。

优化

用搜索中的方向数组,可以使代码长度更短。

代码

#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)\) 转移过来,状态转移方程如下:

\[dp[S][j] = \min(dp[S-\{j\}][i] + dis[i][j]) \]

最终答案由于要回到北京,所以如下:

\[\text{ans}=\min(dp[i][0]+dis[i][0]) \]

时间复杂度 \(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 问题,考虑剪枝。

  1. 如果当前数量和剩余能选数字之和小于等于最大数量,直接返回。
  2. 去除掉原先就有的完全平方数,然后进行类似搜索的标记数组,再回溯求解。

答案

答案为 \(\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 问题啊?(恼)

posted @ 2026-01-23 12:50  TangyixiaoQAQ  阅读(2)  评论(0)    收藏  举报