[学习笔记\练习记录]特殊动态规划 及 动态规划的一些优化技巧

仅记录我不太熟悉 及 有待加强的类型。

因为动态规划对于题目有较强依赖性,所以结合题目一起写了。

\(dp\) 套 \(dp\)

[TJOI2018]游园会

考虑设计一个自动机处理\(LCS\)的限制。

设\(f_{i,j,k}\)为前\(i\)个字符,转移到了自动机的\(j\)节点,匹配到了\(NOI\)的第\(k\)位,即可以转移了。

那么考虑如何构建这个自动机。

\(g_{i,j}\)为第一个串匹配到\(i\),第二个串匹配到\(j\),由于第一个串是我们转移的串,转移只需知道\(g_{i - 1,x}\)和新加入的字符即可,考虑记录\(g_{i,x}\)为状态,状压其差分数组即可。

考虑每一次都是从上一层转移到下一层,我们无需直接构建该自动机,在\(dp\)时处理其即可。

[TJOI2018]游园会
#include<bits/stdc++.h>
#define ll long long 
#define N 1010
#define mod 1000000007
#define popcount(S) __builtin_popcount(S)

int n,k,tot,ans[N];
char s[N];
int f[2][1 << 15][3],g[2][N];

void decode(int S) {
	for (int i=1;i<=k;++i) g[0][i]=(S>>(i-1))&1;
	for (int i=1;i<=k;++i) g[0][i]+=g[0][i-1];
}
int encode() {
	int S=0;
	for (int i=1;i<=k;++i)
		if (g[1][i]-g[1][i-1]) S|=1<<(i-1);
	return S;
}

void trans(int nxt,int S,int l,char c,int w) {
	decode(S);
	for (int i=1;i<=k;++i) {
		g[1][i]=std::max(g[1][i-1],g[0][i]);
		if (c==s[i]) g[1][i]=std::max(g[1][i],g[0][i-1]+1);
	}
	int j=encode();
	f[nxt][j][l]=(f[nxt][j][l]+w)%mod;
}

int main(){
	scanf("%d%d",&n,&k);
	scanf("%s",s + 1);
	tot = (1ll << k);
	f[0][0][0] = 1;
	for(int i = 0;i < n;++i){
		int now = i & 1;
		int nxt = 1^now;
		memset(f[nxt],0,sizeof(f[nxt]));
		for(int j = 0;j < tot;++j){
			if(f[now][j][0]){
				trans(nxt,j,1,'N',f[now][j][0]);
				trans(nxt,j,0,'O',f[now][j][0]);
				trans(nxt,j,0,'I',f[now][j][0]);				
			}
			if(f[now][j][1]){
				trans(nxt,j,1,'N',f[now][j][1]);
				trans(nxt,j,2,'O',f[now][j][1]);
				trans(nxt,j,0,'I',f[now][j][1]);				
			}
			if(f[now][j][2]){
				trans(nxt,j,1,'N',f[now][j][2]);
				trans(nxt,j,0,'O',f[now][j][2]);
			}						
		}
	}
	for (int i=0;i<tot;++i)
		for (int j=0;j<3;++j)
			ans[popcount(i)]=(ans[popcount(i)]+f[n&1][i][j])%mod;
	for (int i=0;i<=k;++i) printf("%d\n",ans[i]);	
}
上一篇:C#.NET面试题汇总系列一:基础语法


下一篇:(4.4B)出租车费