1. 程式人生 > >SCOI2008著色方案(記憶化搜尋)

SCOI2008著色方案(記憶化搜尋)

有n個木塊排成一行,從左到右依次編號為1~n。你有k種顏色的油漆,其中第i 種顏色的油漆足夠塗ci 個木塊。所有油漆剛好足夠塗滿所有木塊,即

c1+c2+...+ck=n。相鄰兩個木塊塗相同色顯得很難看,所以你希望統計任意兩個相鄰木塊顏色不同的著色方案。

Solution

有一個非常好的條件就是c[i]<=5,這樣我們就可以設計狀態為dp[1][2][3][4][5][la]表示有一個的有幾種顏色,有兩個的有幾種顏色·····,上一次我們列舉的是哪裡。

然後就愉快的記憶化搜尋,注意要求相鄰木塊顏色不同的條件:例如我當前列舉1,如果我上一次列舉的二,那麼有一個一我就不能選。

Code

#include<iostream>
#include<cstring>
#include<cstdio>
#define mod 1000000007
using namespace std;
typedef long long ll;
int dp[16][16][16][16][16][6],n,ji[9],x;
int dfs(int one,int sec,int thi,int fou,int fiv,int hea){
    if(~dp[one][sec][thi][fou][fiv][hea])return dp[one][sec][thi][fou][fiv][hea];
    
long long ans=0; if(one)(ans+=((ll)one-(hea==1))*dfs(one-1,sec,thi,fou,fiv,0))%=mod; if(sec)(ans+=((ll)sec-(hea==2))*dfs(one+1,sec-1,thi,fou,fiv,1))%=mod; if(thi)(ans+=((ll)thi-(hea==3))*dfs(one,sec+1,thi-1,fou,fiv,2))%=mod; if(fou)(ans+=((ll)fou-(hea==4))*dfs(one,sec,thi+1,fou-1,fiv,3
))%=mod; if(fiv)(ans+=(ll)fiv*dfs(one,sec,thi,fou+1,fiv-1,4))%=mod; return dp[one][sec][thi][fou][fiv][hea]=ans; } int main(){ scanf("%d",&n); memset(dp,-1,sizeof(dp)); for(int i=0;i<=5;++i)dp[0][0][0][0][0][i]=1; for(int i=1;i<=n;++i)scanf("%d",&x),ji[x]++; printf("%d",dfs(ji[1],ji[2],ji[3],ji[4],ji[5],5)); return 0; }