考试 T2,没想到真的就是一个大爆搜啊~
搜索时几个剪枝:
1.剩余可用颜色要大于等于剩余步数
2.颜色从没用过的里找,然后用二进制状压存储.
3. 最重要的一个剪枝:如果一个颜色在之前序列中未出现过,且在之后的位置也不会强制出现,则所有这种情况的贡献都是相同的,只需算第一次的贡献,然后后面的依次累加即可.
code:
#include <bits/stdc++.h> using namespace std; void setIO(string s) { string in=s+".in"; string out=s+".out"; freopen(in.c_str(),"r",stdin); // freopen(out.c_str(),"w",stdout); } const int mod=1e9+7; int lg[1040],n,m,k,a[23][23],f[23][23],v[23]; int dfs(int x,int y) { if(y==m+1) ++x,y=1; if(x==n+1) return 1; int s=f[x-1][y]|f[x][y-1],calc=-1,ret=0; int S=~s&((1<<k)-1); if(n+m-x-y+1>lg[S]) return 0; for(int tmp=0;tmp<k;++tmp) { if(S&(1<<tmp)) { if(a[x][y]==0||a[x][y]==tmp+1) { v[tmp+1]++; f[x][y]=s|(1<<tmp); if(v[tmp+1]==1) { if(calc==-1) calc=dfs(x,y+1); ret+=calc; } else ret+=dfs(x,y+1); if(ret>=mod) ret-=mod; v[tmp+1]--; } } } return ret; } int main() { // setIO("input"); int i,j; for(i=1;i<1024;++i) lg[i]=lg[i>>1]+(i&1); scanf("%d%d%d",&n,&m,&k); if(n+m-1>k) { printf("0 "); return 0; } for(i=1;i<=n;++i) { for(j=1;j<=m;++j) scanf("%d",&a[i][j]), v[a[i][j]]++; } printf("%d ",dfs(1,1)); return 0; }