#10172. 「一本通 5.4 練習 1」塗抹果醬 題解
阿新 • • 發佈:2018-10-28
tst num gist 輸出 def head printf admin line
題目鏈接
一道三進制狀壓的好題。
題目描述:
Tyvj 兩周年慶典要到了,Sam 想為 Tyvj 做一個大蛋糕。蛋糕俯視圖是一個 N×M的矩形,它被劃分成 N×M個邊長為 1×1的小正方形區域(可以把蛋糕當成 N 行 M 列的矩陣)。
蛋糕很快做好了,但光禿禿的蛋糕肯定不好看!
所以,Sam 要在蛋糕的上表面塗抹果醬。果醬有三種,分別是紅果醬、綠果醬、藍果醬,
三種果醬的編號分別為 1,2,3.為了保證蛋糕的視覺效果,Admin 下達了死命令:
相鄰的區域嚴禁使用同種果醬。但 Sam 在接到這條命令之前,
已經塗好了蛋糕第 KKK 行的果醬,且無法修改。 現在 Sam 想知道:能令 Admin 滿意的塗果醬方案有多少種。請輸出方案數 mod1e6。若不存在滿足條件的方案,請輸出 0。
輸入格式
輸入共三行。
第一行:N,M;
第二行:K;
第三行:M 個整數,表示第 K 行的方案。
字母的詳細含義見題目描述,其他參見樣例。
輸出格式
輸出僅一行,為可行的方案總數。
樣例
樣例輸入
2 2 1 2 3
樣例輸出
3
解題思路:
這道題關鍵在判斷合法情況,第k行特判一下即可。
1.判斷一個三進制數是否有相同數字相鄰的情況,不能模擬二進制左移右移的情況,
因為這裏有3個數字,左移右移會出現有0的影響。
2.判斷不同行是否有相同數字相鄰,模擬二進制的&判斷一下即可。
代碼:
#include<bits/stdc++.h> #define ll long long #define R register using namespace std; int n,m,k,mod=1e6,a[250],sk,num,top,ans,f[10005][250]; inline int ksm(R int x,R int p) { R int tot=1; while(p) { if(p&1) { tot=tot*x; } x=x*x; p>>=1; } return tot; } inline int check(R int x,R int y) { for(R int i=1;i<=m;++i){ if((x%3)==(y%3))return 0; x/=3; y/=3; } return 1; } inline int judge(R int x) { R int y=-1; for(R int i=1;i<=m;++i) { if(y==x%3)return 0; y=x%3; x/=3; } return 1; } inline void init() { for(R int i=0;i<=242;++i){ R int x=i,tot=0; while(x) { x/=3; ++tot; } if(tot>=m+1)break; if(judge(i)){ a[++num]=i; if(i==sk)top=num; } } } int main(){ scanf("%d%d",&n,&m); scanf("%d",&k); for(R int i=1;i<=m;++i) { R int t; scanf("%d",&t); sk+=(t-1)*ksm(3,i-1); } if(!judge(sk)) { printf("0"); return 0; } init(); if(k==1) f[1][top]=1; else for(R int i=1;i<=num;++i) f[1][i]=1; for(R int i=2;i<=n;++i)//當前第幾行 { if(i==k) { for(R int t=1;t<=num;++t) if(check(a[top],a[t])) f[i][top]=(f[i][top]+f[i-1][t])%mod; } else { for(R int j=1;j<=num;++j)//當前行狀態 { if(i-1==k) { if(check(a[j],a[top])) f[i][j]=(f[i][j]+f[i-1][top])%mod; } else { for(R int t=1;t<=num;++t)//上一行狀態 if(check(a[j],a[t])) f[i][j]=(f[i][j]+f[i-1][t])%mod; } } } } for(R int i=1;i<=num;++i) ans=(ans+f[n][i])%mod; printf("%d",ans%mod); return 0; }
這道題關鍵在於舍棄不合法情況的判斷.
#10172. 「一本通 5.4 練習 1」塗抹果醬 題解