解题报告 匹配数
2023-07-16
DP
这是一篇古老的文章(它通常创建于至少 2 年前),其中的内容可能已经过时。

老题解批量补档。


http://222.180.160.110:61235/contest/3887/problem/2

求出最小的、不含前导零的 \(n\) 位数 \(x\),满足 \(n-1\) 条限制,第 \(i\) 条限制规定 \(x\) 的第 \(i\) 位和 \(i + 1\) 位的关系(小于、大于、等于、不等于)。

如果正着 DP,也就是说先确定前面的数位再向后 DP,后面的数位就没办法决定选择哪个已有状态进行转移,因为我们没有办法仅凭上一位就得到哪个状态拥有最小的字典序。

但是题解很风轻云淡地给出了一个我一辈子想不出来的 fix:倒着 DP。我们只要保证每次选取最小的可行的下一位即可,这恰好符合字典序的定义。

记录前驱(or 后继?)后输出即可。

namespace XSC062 {
using namespace fastIO;
const int maxm = 15;
const int maxn = 2e3 + 5;
int n;
char s[maxn];
int f[maxn][maxm];
void output(int i, int j) {
    print(j);
    if (i != n)
        output(i + 1, f[i][j]);
    return;
}
int main() {
    scanf("%s", s + 1);
    n = strlen(s + 1) + 1;
    memset(f, -1, sizeof (f));
    for (int i = 0; i <= 9; ++i)
        f[n][i] = 0x3f3f3f3f;
    for (int i = n - 1; i; --i) {
        for (int j = 0; j <= 9; ++j) {
            if (s[i] == '>') {
                for (int k = j - 1; k >= 0; --k) {
                    if (~f[i + 1][k])
                        f[i][j] = k;
                }
            }
            else if (s[i] == '<') {
                for (int k = 9; k > j; --k) {
                    if (~f[i + 1][k])
                        f[i][j] = k;
                }
            }
            else if (s[i] == '=') {
                if (~f[i + 1][j])
                    f[i][j] = j;
            }
            else {
                for (int k = 9; ~k; --k) {
                    if (k == j)
                        continue;
                    if (~f[i + 1][k])
                        f[i][j] = k;
                }
            }
        }
    }
    for (int i = 1; i <= 9; ++i)
        if (~f[1][i]) {
            output(1, i);
            break;
        }
    return 0;
}
} // namespace XSC062

言论