状态压缩,我们枚举第一行的所有状态,然后根据第一行去转变下面的行,枚举或者深搜直到最后最后一行,可以判断是不是所有的1都可以全部转换为0,记录所有的解,输出最小的一个就可以.
这里有一个很重要的优化,就是当n比m大的,转置这个矩阵,如果不加这个在G++的情况下会超时,C++900Ms多AC.代码及注释如下
#include<stdio.h> #include<iostream> #include<string.h> #include<queue> #include<algorithm> using namespace std; #define inf 0x3f3f3f int row[20],newrow[20],n,m; int dfs(int id,int prerow,int tot) { if(id == n) { if(prerow != 0)return inf; else return tot; } int now = newrow[id];///这是当前行 for(int j = 0; j < m; j++) if(prerow & (1<<j))///now(当前行)受prerow(前一行)的限制,只有prenow的这一列也是X的时候才可以反转 { tot++; now ^= 1<<j; if(j > 0)now ^= 1<<(j-1); if(j < m-1)now ^= 1<<(j+1); if(id+1 < n)newrow[id+1] ^= 1<<(j);///当前行的下一行 } dfs(id+1,now,tot); } int main() { char maps[20][20]; while(~scanf("%d%d",&n,&m)) { if(n == 0 && m == 0) return 0; for(int i = 0; i < n; i++) scanf("%s",maps[i]); if(n >= m) { for(int i = 0; i < n; i++) { row[i] = 0; for(int j = 0; j < m; j++) if(maps[i][j] == 'X') row[i] |= (1<<j); } } else///转置优化 { for(int i = 0; i < m; i++) { row[i] = 0; for(int j = 0; j < n; j++) if(maps[j][i] == 'X') row[i] |= (1<<j); } swap(n,m); } int ans = inf,tot; for(int i=0; i<(1<<m); i++)///枚举第一行的所有状态 { tot = 0; for(int j = 0; j < n; j++) newrow[j] = row[j]; for(int j=0; (1<<j)<=i; j++) if(i & (1<<j))///如果是X就反转,并且带动周围4个点,边界需要特判 { tot++; newrow[0] ^= (1<<j); if(j > 0)newrow[0] ^= (1<<(j-1)); if(j < m-1)newrow[0] ^= (1<<(j+1)); if(n > 1) newrow[1] ^= (1<<j); } tot = dfs(1,newrow[0],tot);///开始搜索到最后一行 if(tot < ans)ans = tot; } if(ans == inf)printf("Damaged billboard. "); else printf("You have to tap %d tiles. ",ans); } return 0; }