題目連結:
首先,可以確定每個格子只能選一次,因為選任何大於0的偶數次,等於沒有效果一樣。
然後,就可以把這題理解成從r*c的矩陣中選擇一些格子進行“點亮”操作,使得最終所有格子都是“亮”的狀態。那麼,每個格子要麼有點亮操作,要麼沒有,總共複雜度為2^25,顯然必須進行減枝。
假設從第一行第一列開始,從左往右,從上往下一次依次選擇,對於當前所在位置(x, y),它已經不能影響到x-2以前的行數了,所以當到x行時,如果第x-2行沒有全部點亮,則進行減枝。
此外,還可以最佳化,把每行的狀態用一個正數表示,列就用這個正數的二進位位來表示。 這樣判斷一行是否全部亮只要O(1)就可以了.
代碼:
/* * uva 10318 - Security Panel * 暴力+減枝 * */#include<cstdio>#include<cstring>#include<iostream>using namespace std;const int MAXN= 6;int n, m;bool pat[3][3];int path[MAXN*MAXN], mat[MAXN], minx;inline void read(){ memset(mat,0, sizeof(mat)); for(int i=0; i<3; ++i){ for(int j=0; j<3; ++j) pat[i][j] = getchar()=='*'?true:false; getchar(); }}inline bool checkRow(int r){ if(mat[r] != (1<<m)-1) return false; return true;}inline bool checkAll(){ //只檢查最後兩行就可以了(總行數大於1) if(n>1 && checkRow(n-1) && checkRow(n-2) || n==1 && checkRow(0)) return true; return false;}inline void cover(int r,int c){ for(int i=r-1; i<=r+1; ++i){ if(i<0 || i>=n) continue; for(int j=c-1; j<=c+1; ++j){ if(j<0 || j>=m) continue; if(pat[i-r+1][j-c+1]) mat[i] ^= (1<<j); } }}bool dfs(int cur, int pathNum){ int r=cur/m, c=cur-r*m; if(r-2>=0 && !checkRow(r-2)) return false ; if(r>=n-2 || cur==n*m){ if(checkAll()){ minx = pathNum; return true; } if(cur==n*m) return false; }if(dfs(cur+1, pathNum)) return true;cover(r, c);path[pathNum] = cur+1;if(dfs(cur+1, pathNum+1)) return true;cover(r, c); return false;}int main(){ int cas=1; while(~scanf("%d%d%*c",&n,&m) && n+m) { read(); printf("Case #%d\n", cas++); if(dfs(0, 0)){ printf("%d", path[0]); for(int i=1; i<minx; ++i) printf(" %d", path[i]); putchar('\n'); }else puts("Impossible."); } return 0; }