[题解] [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; 
}
posted @ 2020-04-28 10:51  ztlztl  阅读(111)  评论(0编辑  收藏  举报