$bzoj1079-SCOI2008$ 着色方案 $dp$

  • 题面描述
    • \(n\)个木块排成一行,从左到右依次编号为\([1,n]\)。你有\(k\)种颜色的油漆,其中第\(i\)种颜色的油漆足够涂\(c_i\)个木块。所有油漆刚好足够涂满所有木块,即\(\sum_{i=1}^k c_i=n\)。相邻两个木块涂相同色显得很难看,所以你希望统计任意两个相邻木块颜色不同的着色方案。
  • 输入格式
    • 第一行为一个正整数\(k\),第二行包含\(k\)个整数\(c_1, c_2, ... , c_k\)
  • 输出格式
    • 输出一个整数,即方案总数模\(1e9+7\)的结果。
  • 题解
    • 根据题意可写出暴力\(dp\)\(f_{i,5,5,\cdots\,5,x}\)时空复杂度爆炸
    • 但是我们其实并不需要记录每个色有多少个,我们只需要记录当前颜色有\(1,2,3,4,5\)个各有几个,\(f_{i,a_1,a_2,a_3,a_4,a_5,x}\)用记忆化搜索即可
  • 备注
    • 用正常方法做,会有很多冗余状态,不能直接用\(cnt_i\)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
ll f[16][16][16][16][16][6];
bool use[16][16][16][16][16][6];
int n;
int cnt[6];
ll dp(int a,int b,int c,int d,int e,int x){
    if (use[a][b][c][d][e][x]) return f[a][b][c][d][e][x];
    if (a+b+c+d+e==0) return 1;
    ll ret=0;
    if (a>0) ret=(ret+(a-(x==2))*dp(a-1,b,c,d,e,1)%mod)%mod;
    if (b>0) ret=(ret+(b-(x==3))*dp(a+1,b-1,c,d,e,2)%mod)%mod;
    if (c>0) ret=(ret+(c-(x==4))*dp(a,b+1,c-1,d,e,3)%mod)%mod;
    if (d>0) ret=(ret+(d-(x==5))*dp(a,b,c+1,d-1,e,4)%mod)%mod;
    if (e>0) ret=(ret+e*dp(a,b,c,d+1,e-1,5)%mod)%mod;
    use[a][b][c][d][e][x]=1;
    return f[a][b][c][d][e][x]=ret%mod;
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        int x; scanf("%d",&x);
        cnt[x]++;
    }
//  memset(f,-1,sizeof(f));
    printf("%lld\n",dp(cnt[1],cnt[2],cnt[3],cnt[4],cnt[5],0));
    return 0;
}
  • 附一个错误的顺序\(dp\)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=76;
int n,cnt[10],tot;
int f[3][16][16][16][16][16][6];
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        int x; scanf("%d",&x);
        cnt[x]++;
        tot+=x;
    }
    n=tot;
    f[1][cnt[1]-1][cnt[2]][cnt[3]][cnt[4]][cnt[5]][1]=cnt[1];
    f[1][cnt[1]+1][cnt[2]-1][cnt[3]][cnt[4]][cnt[5]][2]=cnt[2];
    f[1][cnt[1]][cnt[2]+1][cnt[3]-1][cnt[4]][cnt[5]][3]=cnt[3];
    f[1][cnt[1]][cnt[2]][cnt[3]+1][cnt[4]-1][cnt[5]][4]=cnt[4];
    f[1][cnt[1]][cnt[2]][cnt[3]][cnt[4]+1][cnt[5]-1][5]=cnt[5];
    for (int i=2;i<=n;i++){
        for (int j=0;j<=15;j++){
        for (int k=0;k<=15;k++){
        for (int l=0;l<=15;l++){
        for (int m=0;m<=15;m++){
        for (int r=0;r<=15;r++){
        for (int x=1;x<=5;x++){
            for (int y=1;y<=5;y++){
            int now=i&1; int pre=now^1;
            if (j>0&&x==1&&x+1==y) f[now][j-1][k][l][m][r][x]+=f[pre][j][k][l][m][r][y]*(j-1);
            if (j>0&&x==1&&x+1!=y) f[now][j-1][k][l][m][r][x]+=f[pre][j][k][l][m][r][y]*j;
            if (k>0&&x==2&&x+1==y) f[now][j+1][k-1][l][m][r][x]+=f[pre][j][k][l][m][r][y]*(k-1);
            if (k>0&&x==2&&x+1!=y) f[now][j+1][k-1][l][m][r][x]+=f[pre][j][k][l][m][r][y]*k;
            if (l>0&&x==3&&x+1==y) f[now][j][k+1][l-1][m][r][x]+=f[pre][j][k][l][m][r][y]*(l-1);
            if (l>0&&x==3&&x+1!=y) f[now][j][k+1][l-1][m][r][x]+=f[pre][j][k][l][m][r][y]*l;
            if (m>0&&x==4&&x+1==y) f[now][j][k][l+1][m-1][r][x]+=f[pre][j][k][l][m][r][y]*(m-1);
            if (m>0&&x==4&&x+1!=y) f[now][j][k][l+1][m-1][r][x]+=f[pre][j][k][l][m][r][y]*m;
            if (r>0&&x==5&&x+1==y) f[now][j][k][l][m+1][r-1][x]+=f[pre][j][k][l][m][r][y]*(r-1);
            if (r>0&&x==5&&x+1!=y) f[now][j][k][l][m+1][r-1][x]+=f[pre][j][k][l][m][r][y]*r;
            }
        }
        }
        }
        }
        }
        }
    }
    int ans=0;
    for (int i=0;i<=15;i++){
        for (int j=0;j<=15;j++){
            for (int k=0;k<=15;k++){
                for (int l=0;l<=15;l++){
                    for (int m=0;m<=15;m++){
                        for (int x=1;x<=5;x++){
                            ans+=f[n&1][i][j][k][l][m][x];
                        }
                    }
                }
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}
posted @ 2019-05-09 16:28  paul120090105  阅读(...)  评论(... 编辑 收藏