• [DP套DP][BZOJ3864][HDU4899] Hero Meet Devil(代码解释)


    嗯。。本人的第二篇blog,想想有点激动。
    其实这篇blog是我在学习DP套DP的时候临时想到要发一篇blog。
    网上关于DP套DP的资料不多,在百度上搜DP套DP大多数都是搜到这道题的题解,虽然题解大致说了DP套DP的原理,但对代码没有多少解释,以至于像我这种蒟蒻根本理解不了啊。。。。
    于是在苟且抄了题解后,根据自己的理解写了注释,也希望对后来的人有帮助。
    注:我是根据Candy? 的blog写的代码。 (其实就是换了变量名QwQ).

    如果不太了解求lcs的dp方程,建议先去看一看。。

    其实 这道题 (因为蒟蒻太弱,不确定是不是DP套DP的套路) 代码分两部分,因为T字符串每个字符在AGCT随便取,所以每一位的转移都是一样的,那么我们可以预处理出状态转移的通式,对于T的每一位都是这样的,那么我们只要转移 m 次就好。

    如果你看了本文仍然不会DP套DP的话,没关系,我也不会。。。
    不用来问我了(雾)。

    #include<cstdio>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #define gch getchar
    #define pch putchar
    #define LL long long
    #define INF 0xfffffff
    using namespace std;
    template <class X> inline void read(X &x){
    	char c=getchar();x=0;X flag=1;
    	while(c>'9'||c<'0') {if(c=='-') flag=-1;c=getchar();}
    	while(c<='9'&&c>='0') {x=x*10+c-48;c=getchar();}
    	x*=flag;
    }
    template <class X> inline void print(X x){
    	if(x<0) {putchar('-');x=-x;}
    	if(x>9) print(x/10);
        putchar(x%10+'0');
    }
    
    const int MAXN=20;
    const int MAXM=1005;
    const int MAX=1<<15|5;
    const int MO=1000000000+7;
    
    int n,m;char s[MAXN],c[MAXN]="ACGT";
    int trans[MAX][4],one[MAX],f[2][MAX];
    int ans[MAXM];
    
    //转移矩阵trans[i][k]:当前LCS状态为i的情况下T串填字符k能到的状态。 
    
    //One[j]:状态 j 对应的最长公共子序列。
    
    inline void init(){
    	static int d[MAXN],g[MAXN];//d[j],g[j]:相当于lcs[i][j]的后面一维。 
    	//两者区别在于,d[j]是lcs[i-1],g[j]是lcs[i];
    	for(int j=0;j<(1<<n);++j){//枚举lcs[i-1]的状态。 
    		for(int i=0;i<n;++i) 
    			d[i+1]=d[i]+((j&(1<<i))?1:0);
    		//因为用差分压缩,所以用前缀和解压。 
    		one[j]=d[n];
    		//One[j]就是j状态对应的最长公共子序列,自然就是我们算出来的d[n]啦。
    		//此后枚举所有可能的状态。 
    		for(int k=0;k<4;++k){//枚举T的某一位(假想的一位,模拟lcs dp 的过程)可能的四种情况:A,C,G,T; 
    			for(int i=1;i<=n;++i){//枚举S的第几位。 
    				g[i]=max(g[i-1],d[i]);
    				if(c[k]==s[i]) g[i]=max(g[i],d[i-1]+1);	
    				//lcs[i][j]=max(lcs[i-1][j],lcs[i][j-1],(a[i]==b[j])lcs[i-1][j-1]+1);
    			}
    			trans[j][k]=0;
    			for(int i=0;i<n;++i)
    				if(g[i+1]-g[i]) trans[j][k]|=1<<i;
    			//把g数组压缩。 
    		}
    	}
    }
    
    int main(){
    	int t;read(t);
    	while(t--){
    		memset(s,0,sizeof s);
    		memset(ans,0,sizeof ans);
    		memset(f,0,sizeof f);
    		scanf("%s",s+1);
    		n=strlen(s+1);read(m);
    		//printf("n:%d m:%d
    ",n,m);
    		init();
    		//预处理出转移矩阵:trans[j][k]和每种状态对应的lcs:one[j];
    		int cur=0;
    		f[0][0]=1;
    		for(int i=1;i<=m;++i){
    			cur^=1;
    			memset(f[cur],0,sizeof f[cur]);
    			for(int j=0;j<(1<<n);j++){
    				for(int k=0;k<4;++k){
    					f[cur][trans[j][k]]+=f[cur^1][j];
    					f[cur][trans[j][k]]%=MO;
    				}
    			}
    		}
    		//状态转移,滚动数组优化一维。 
    		for(int j=0;j<(1<<n);++j){
    			ans[one[j]]+=f[cur][j];
    			ans[one[j]]%=MO;
    		}
    		for(int i=0;i<=n;++i){
    			print(ans[i]);pch('
    ');
    		}
    	}
    	return 0;
    }
    
  • 相关阅读:
    SpringMvc@RequestParam 来映射请求参数
    SpringMvc中@PathVariable注解简单的用法
    SpringMvc的学习之路
    平常的操作函数以及回调
    关于弹窗 取消 确定清空按钮的事件
    判断主表里子表不能添加相同的字段
    选择company回显appname
    树形菜单数据源
    .NET为什么要使用异步(async)编程?⭐⭐⭐⭐⭐
    通过HttpClient的方式去Curd数据⭐⭐⭐⭐
  • 原文地址:https://www.cnblogs.com/jacktangs/p/9379669.html
Copyright © 2020-2023  润新知