[BZOJ1079/Luogu2476][SCOI2008]著色方案
阿新 • • 發佈:2018-12-26
題目連結:
記憶化搜尋+\(DP\)。
首先看到資料範圍就知道狀態是個\(15^5\)的級別而不是\(5^{15}\)了。
那麼顯然設\(f_{a,b,c,d,e}\)表示還有\(a\)種顏色剩\(1\)個,\(b\)種剩兩個,\(\cdots\),\(e\)種剩\(5\)個時的方案數。
但是如何判斷兩個顏色不能相鄰呢?
如果記錄上一次的顏色,那麼這次判斷也需要顏色,則狀態爆炸。
發現無論上次是什麼顏色,只需要判斷是否相同即可。
那麼就記錄上一次選的顏色現在還剩幾個,在轉移時如果相同那麼方案數就要少一種選擇。
綜上,我們得到了如下的轉移式:
\[f_{a,b,c,d,e,PreRem}+=f_{a-1,b,c,d,e,0}*(a-[PreRem==1]) (a>0)\]
其中\(PreRem\)代表上一次選的顏色現在剩幾個。
如果還有\(a\)個顏色\(1\),那麼可以更新答案,\(PreRem\)變為\(0\),此時有\(a\)種顏色可選,故答案\(*a\)。
同時如果\(PreRem==1\),那麼說明這\(a\)種顏色中有一種與之前重複,不能選,只剩\(a-1\)種顏色可選,答案\(*(a-1)\)。
對於其他\(4\)項轉移方程也同理(別忘記在低項加上剛用掉的顏色)。
時間複雜度 \(O(k^c)\)
#include <cstdio> #include <cstring> int k,c[10]; int f[16][16][16][16][16][6]; const int Mod=1000000007; int DP(int a,int b,int c,int d,int e,int PreRem) { if(!a&&!b&&!c&&!d&&!e)return 1; int &Ans=f[a][b][c][d][e][PreRem]; if(~Ans)return Ans; Ans=0; if(a)(Ans+=DP(a-1,b,c,d,e,0)*1LL*(PreRem==1?a-1:a)%Mod)%=Mod; if(b)(Ans+=DP(a+1,b-1,c,d,e,1)*1LL*(PreRem==2?b-1:b)%Mod)%=Mod; if(c)(Ans+=DP(a,b+1,c-1,d,e,2)*1LL*(PreRem==3?c-1:c)%Mod)%=Mod; if(d)(Ans+=DP(a,b,c+1,d-1,e,3)*1LL*(PreRem==4?d-1:d)%Mod)%=Mod; if(e)(Ans+=DP(a,b,c,d+1,e-1,4)*1LL*(PreRem==5?e-1:e)%Mod)%=Mod; return Ans; } int main() { scanf("%d",&k),memset(f,-1,sizeof f); for(int cs;k--;)scanf("%d",&cs),++c[cs]; printf("%d\n",DP(c[1],c[2],c[3],c[4],c[5],0)); return 0; }