[题解] [CF938F] Erasing Substrings
题面
题解
发现这个操作的使用顺序是独立的
所以后面的操作我们可以提前用, 前面的操作可以后面再去用
考虑我们当前已经知道了最优解的前 \(i\) 项, 现在在求第 \(i + 1\) 项
要达到最优解的方式当然有很多, 我们使用过的操作集合为 \(s[]\)
那么我们可以任选没有用过的操作, 设使用某些没有用过的操作之后, 集合变为了 \(t[]\)
由于我们知道我们在求第几项, 也可以根据 \(t[]\) 集合知道我们删掉了几个数
所以我们可以推出使用集合为 \(t[]\) 时, 当前可以加入的字符是哪一个
在所有这些可加入的字符中选取最优的
那些达不到最优的使用集合 \(t[]\) 在更新 \(i + 2\) 的时候就不能作为 \(i + 1\) 的 \(s[]\) 了
因为它代表的并不是最优的
这样我们就可以从前 \(i\) 项的最优解推到第 \(i + 1\) 项了
那么其实就是一个这样的思想
每次从所有最优的情况把接下来合法的情况遍历一遍
在这些合法的情况中选出最优的
再把那些不是最优的合法情况置成不合法
设 \(f[i][s]\) 为选取操作集合为 \(s\) 的时候, 最优解的第 \(i\) 项最优是多少
转移就是普通的状压
第 \(i\) 这一维可以滚掉
具体实现可以看一下代码
code
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <cmath>
const int N = 5005;
using namespace std;
int n, f[N], k, sz, lst;
char s[N];
template < typename T >
inline T read()
{
T x = 0, w = 1; char c = getchar();
while(c < '0' || c > '9') { if(c == '-') w = -1; c = getchar(); }
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x * w;
}
int main()
{
scanf("%s", s + 1), n = strlen(s + 1);
for(int i = 0; i <= n; i++)
f[i] = 1;
k = log2(n), sz = 1 << k, lst = n - sz + 1;
for(int i = 1; i <= lst; i++)
{
char ch = 'z' + 1;
for(int j = 0; j < sz; j++)
if(f[j]) for(int l = 0; l < k; l++) f[j | (1 << l)] = 1;
for(int j = 0; j < sz; j++)
if(f[j]) ch = min(ch, s[i + j]);
for(int j = 0; j < sz; j++) f[j] &= (s[j + i] == ch);
putchar(ch);
}
puts("");
return 0;
}