zoukankan      html  css  js  c++  java
  • DP套DP

    DP套DP,就是将内层DP的结果作为外层DP的状态进行DP的方法。

    [BZOJ3864]Hero meet devil

    对做LCS的DP数组差分后状压,预处理出转移数组,然后直接转移即可。

    tr[S][k]表示当前差分状压后的状态为S,加入字符k(k为ACGT中一个)后会转移到什么状态。

    f[i][S]表示串已构造到第i位,和模式串的匹配状态差分后为S,的方案数。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
     5 typedef long long ll;
     6 using namespace std;
     7 
     8 const int N=1010,M=50010,mod=1e9+7;
     9 int T,n,m,d[20],g[20],ans[N],tr[M][4],cnt[M],f[2][M];
    10 char s[20],c[10]="ACGT";
    11 
    12 int main(){
    13     freopen("bzoj3864.in","r",stdin);
    14     freopen("bzoj3864.out","w",stdout);
    15     for (scanf("%d",&T); T--; ){
    16         scanf("%s%d",s+1,&m); n=strlen(s+1);
    17         for (int S=0; S<1<<n; S++){
    18             if (S) cnt[S]=cnt[S^(S&-S)]+1;
    19             rep(i,0,n-1) d[i+1]=d[i]+((S>>i)&1);
    20             rep(k,0,3){
    21                 rep(i,1,n) g[i]=max(max(d[i],g[i-1]),(c[k]==s[i])?d[i-1]+1:0);
    22                 tr[S][k]=0;
    23                 rep(i,0,n-1) if (g[i+1]-g[i]) tr[S][k]|=1<<i;
    24             }
    25         }
    26         memset(ans,0,sizeof(ans)); memset(f,0,sizeof(f)); f[0][0]=1;
    27         rep(i,1,m){
    28             int v=i&1,u=v^1; memset(f[v],0,sizeof(f[v]));
    29             for (int S=0; S<1<<n; S++)
    30                 rep(k,0,3) f[v][tr[S][k]]=(f[v][tr[S][k]]+f[u][S])%mod;
    31         }
    32         for (int S=0; S<1<<n; S++) ans[cnt[S]]=(ans[cnt[S]]+f[m&1][S])%mod;
    33         rep(i,0,n) printf("%d
    ",ans[i]);
    34     }
    35     return 0;
    36 }

    [BZOJ5336][TJOI2018]party

    同上题,多加一维0/1/2表示末尾和'NOI'匹配到第几位即可。

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
     5 typedef long long ll;
     6 using namespace std;
     7 
     8 const int N=1010,M=50010,mod=1e9+7;
     9 int T,n,m,d[20],g[20],ans[N],tr[M][4],cnt[M],f[2][M][3];
    10 char s[20],c[10]="NOI";
    11 
    12 int main(){
    13     freopen("bzoj5336.in","r",stdin);
    14     freopen("bzoj5336.out","w",stdout);
    15     scanf("%d%d%s",&m,&n,s+1);
    16     for (int S=0; S<1<<n; S++){
    17         if (S) cnt[S]=cnt[S^(S&-S)]+1;
    18         rep(i,0,n-1) d[i+1]=d[i]+((S>>i)&1);
    19         rep(k,0,2){
    20             rep(i,1,n) g[i]=max(max(d[i],g[i-1]),(c[k]==s[i])?d[i-1]+1:0);
    21             tr[S][k]=0;
    22             rep(i,0,n-1) if (g[i+1]-g[i]) tr[S][k]|=1<<i;
    23         }
    24     }
    25     f[0][0][0]=1;
    26     rep(i,1,m){
    27         int v=i&1,u=v^1; memset(f[v],0,sizeof(f[v]));
    28         for (int S=0; S<1<<n; S++) rep(l,0,2) if (f[u][S][l]){
    29             rep(k,0,2){
    30                 int S1=tr[S][k],l1=l;
    31                 l1=(k==l) ? l1+1 : ((!k)?1:0);
    32                 if (l1<3) f[v][S1][l1]=(f[v][S1][l1]+f[u][S][l])%mod;
    33             }
    34         }
    35     }
    36     for (int S=0; S<1<<n; S++)
    37         rep(i,0,2) ans[cnt[S]]=(ans[cnt[S]]+f[m&1][S][i])%mod;
    38     rep(i,0,n) printf("%d
    ",ans[i]);
    39     return 0;
    40 }

    [BZOJ3591]最长上升子序列

    给出1~n的一个排列的一个最长上升子序列,求原排列可能的种类数。

    考虑最长上升子序列$O(nlog n)$算法中使用的单调队列,将单调队列中的数组成的集合作为状态DP。

    f[S]表示当前状态S的方案数,其中S是一个三进制数,第i位为0表示这个数尚未被选入数列,1表示选入数列但不在单调队列中,2表示在单调队列中。

    对于每个状态,枚举下一个加入序列的数是什么来转移。可以先预处理减小常数。

    具体细节可看代码注释,$O(2^n n^2+3^n n)$

     1 #include<cstdio>
     2 #include<cstring>
     3 #include<algorithm>
     4 #define rep(i,l,r) for (int i=(l); i<=(r); i++)
     5 using namespace std;
     6 
     7 const int N=16,M=1.5e7;
     8 int n,m,x,id[N],p[N],f[M];
     9 bool flag[1<<N][N];
    10 int v[1<<N][N],c[1<<N],mx[1<<N],s[1<<N],ans=0;
    11 
    12 int main(){
    13     freopen("bzoj3591.in","r",stdin);
    14     freopen("bzoj3591.out","w",stdout);
    15     scanf("%d%d",&n,&m);
    16     rep(i,1,m) scanf("%d",&x),id[x-1]=i;
    17     p[0]=1; rep(i,1,n) p[i]=p[i-1]*3;
    18     for (int S=0; S<(1<<n); S++)//预处理出每个可能的状态转移,S为当前已经选进单调队列的集合,flag表示能否转移,v是转移到什么位置
    19         rep(i,0,n-1){
    20             if (S&(1<<i)){ c[S]+=p[i]; mx[S]=max(mx[S],i); s[S]++; continue; }
    21             //c:全1的三进制状态,mx:最大数,s:popcount
    22             if (!id[i]) flag[S][i]=1;
    23             else{
    24                 int tot=0;
    25                 rep(j,0,n-1) if (S&(1<<j) && id[j]) tot++;
    26                 if (tot+1==id[i]) flag[S][i]=1;//满足在LIS中的位置为id[i]
    27             }
    28             int t=-1,x=0;
    29             rep(j,0,n-1) if (S&(1<<j)) x+=p[j];//S转三进制
    30             for (int j=n-1; j>i; j--) if (S&(1<<j)) t=j;//找到集合中比i大的最小的数更新
    31             if (~t) v[S][i]=x-p[t]+p[i]; else v[S][i]=x+p[i];
    32         }
    33     f[0]=1;
    34     for (int x=0; x<(1<<n); x++)
    35         for (int y=x; y>0 || (!y && !x); (!x) ? y-- : y=(y-1)&x)//x为已在序列中的数的集合,y为在单调队列中数的集合
    36             if (f[c[x]+c[y]]){
    37                 int t=c[x]+c[y];
    38                 for (int i=0; s[y]==m ? i<mx[y] : i<n; i++)//枚举新加进序列的数(若单调队列长度已满则不能加入更大的数)
    39                     if (flag[x][i]) f[c[x]+p[i]+v[y][i]]+=f[t];//新加进序列的数一定也加进单调队列
    40                 if (x==(1<<n)-1) ans+=f[t];
    41             }
    42     printf("%d
    ",ans);
    43     return 0;
    44 }

     

  • 相关阅读:
    C++编写ATM(2)
    【Python排序搜索基本算法】之Dijkstra算法
    Java中List转换为数组,数组转List
    [置顶] 亚信联创实习笔记
    PL/SQL 异常处理程序
    CSS position财产
    malloc()与calloc差异
    Qt5官方demo分析集10——Qt Quick Particles Examples
    栈和堆之间的差
    深入浅出JMS(一)——JMS简要
  • 原文地址:https://www.cnblogs.com/HocRiser/p/9722577.html
Copyright © 2011-2022 走看看