扩展dp记录内容减少dp状态:ICPC2021沈阳G

https://vjudge.net/contest/593228#problem/I

场上想的思路是 d p [ s ] [ i ] dp[s][i] dp[s][i] 现在还有 s s s 的没填,从 i i i 位置开始,最后的串,通过记忆化搜索来减少状态,但是还是过不了。


我们考虑继续扩展dp状态存的东西。 d p [ s ] dp[s] dp[s] 表示 s s s 已填,串的字典序最大和endposs的最大位置。然后 s s s 的补集必须在后面全部出现,因此我们就有一个右边界限制。假设我们枚举最后一个字符是 c c c,左边界就是 d p [ s − c ] . e n d p o s s dp[s-c].endposs dp[sc].endposs

#include
using namespace std;
#ifdef LOCAL
 #define debug(...) fprintf(stdout, ##__VA_ARGS__)
#else
 #define debug(...) void(0)
#endif
//#define int long long
inline int read(){int x=0,f=1;char ch=getchar(); while(ch<'0'||
ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;}
#define Z(x) (x)*(x)
#define pb push_back
#define fi first
#define se second
//srand(time(0));
#define N 1010 
#define M 22
int n, m, i, j, k, T;
struct PPPP  {
	int a, b, c; 
};
struct node {
	int pos, v[M]; 
	void mem() { memset(v, 0, sizeof(v)); pos=0; }
	node operator +(const PPPP &p) const {
		node A; A=(*this); 
		A.v[p.a]+=p.b; A.pos=p.c; 
		return A; 
	}
	void print() { for(int i=0; i<m; ++i) debug("%d ", v[i]); debug("\n"); }
}dp[1<<M];
unordered_map<int, node>mp; 
int g[M][N], pre[N][M], nxt[N][M]; 
int f[1<<M], s, t, tot; 
char str[N]; 

node max(node A, node B) {
	for(int k=m; k>=1; --k)
		if(A.v[k]>B.v[k]) return A; 
		else if(B.v[k]>A.v[k]) return B; 
	if(A.pos<B.pos) return A; 
	return B; 
}

signed main()
{
	#ifdef LOCALd
	  freopen("in.txt", "r", stdin);
	  freopen("out.txt", "w", stdout);
	#endif
//	T=read();
//	while(T--) {
//
//	}
	int c[M]; 
	memset(c, 0, sizeof(c)); 
	n=read(); scanf("%s", str+1); 
	for(i=1; i<=n; ++i) str[i]-='a'; 
	for(i=1, k=0; i<=n; ++i) if(!c[str[i]]) c[str[i]]=++k; //debug("c[%c]=%d\n", str[i]+'a', k); 
	m=k; //|S|
	for(i=1; i<=n; ++i) str[i]=c[str[i]]-1; 
	for(i=1; i<=n; ++i) g[str[i]][i]++; 
	for(k=0; k<m; ++k) partial_sum(g[k]+1, g[k]+n+1, g[k]+1);  
	memset(c, 0, sizeof(c)); 
	for(i=n; i>=0; --i) {
		c[str[i]]=i; 
		memcpy(nxt[i], c, sizeof(c)); 
	}
	memset(c, 0, sizeof(c)); 
	for(i=1; i<=n+1; ++i) {
		memcpy(pre[i], c, sizeof(c)); 
		c[str[i]]=i; 
	}
	f[0]=n+1; 
	for(s=0; s<(1<<m); ++s) {
		for(k=0; k<m; ++k) if((s>>k)&1) {
			t=s-(1<<k); 
			if(!pre[f[t]][k]) continue; 
			f[s]=max(f[s], pre[f[t]][k]); 
		}
//		debug("f[%d] = %d\n", s, f[s]); 
	}
	dp[0].mem(); 
	for(s=1; s<(1<<m); ++s) {
		int cnt=__builtin_popcount(s); 
		dp[s].mem(); 
		for(k=0; k<m; ++k) if((s>>k)&1) {
			int t=s-(1<<k), cs=(1<<m)-1-s; 
			int l=dp[t].pos+1, r=f[cs]-1; 
//			debug("%d %d : [%d %d]\n", s, k, l, r); 
			if(r<l) continue; 
			int count=g[k][r]-g[k][l-1], lst=pre[r+1][k]; 
			if(!cnt) continue; 
			dp[s]=max(dp[s], dp[t]+(PPPP){m-cnt+1, count, lst}); 
		}
//		debug("dp[%d].pos = %d\n", s, dp[s].pos); 
	}
	auto t=dp[(1<<m)-1]; 
	for(i=m; i>=1; --i) {
		for(j=1; j<=t.v[i]; ++j) printf("%c", 'a'+i-1); 
	}
	return 0;
}


你可能感兴趣的:(状压dp,压缩状态)