题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1647
解:记得这道题在我初二的时候,老师给过一道差不多的题,唯一区别在于,那道题是打枪影响五个,这道题是翻瓦片。
唔,首先这道题是一个搜索,我们发现我们先确定了第一行哪些要翻,哪些不要翻,之后,第一行的状态就确定了,如果我们还想把第一行上的黑瓦片翻成白瓦片的话,就需要在下一行当前黑瓦片的正下方打一枪,而且必须在那里打,所以我们只要枚举第一行哪些要翻哪些不要翻,接下来的翻的次数就可以确定了。如果,翻完后,最后一行全为白瓦片就可以拿去更新答案了。至于字典序的问题,我是从0枚举到1的,所以越早得到的答案,字典序越优。
复杂度:O((2^n)*n*m)(我也没仔细计算过)
具体的话看一下程序吧。
#include<iostream> #include<cstdio> #include<cstring> #include<string> #define INF 2100000000 using namespace std; int n,m,ans1,tot[20][20],h[5]={0,-1,1,0,0},l[5]={0,0,0,-1,1},a[20][20]; int ans2[20][20],s[20],b[20][20]; bool check(int x,int y) { if ((x<=0)||(x>m)||(y>n)||(y<=0)) return false; return true; } void dfs(int x) { if (x==n+1) //如果第一行的翻与不翻的状态已经确定下来了,就进行所需次数的统计 { for (int i=1;i<=m;i++) for (int j=1;j<=n;j++) a[i][j]=b[i][j]; int sum=0; for (int i=1;i<=n;i++) if (s[i]==1) { tot[1][i]=1; for (int j=0;j<=4;j++) if (check(1+h[j],i+l[j])) a[1+h[j]][i+l[j]]=a[1+h[j]][i+l[j]]^1; //改变为相反的状态 sum++; } else tot[1][i]=0; for (int i=2;i<=m;i++) for (int j=1;j<=n;j++) if (a[i-1][j]==1) //由上一行来决定这一行是否要翻 { tot[i][j]=1; for (int k=0;k<=4;k++) if (check(i+h[k],j+l[k])) a[i+h[k]][j+l[k]]=a[i+h[k]][j+l[k]]^1; sum++; } else tot[i][j]=0; for (int i=1;i<=n;i++)if (a[m][i]==1) return ; //如果最后一行还有黑瓦片,那么这种方案是不合法的,直接退出 //更新答案 if (sum<ans1) { ans1=sum; for (int i=1;i<=m;i++) for (int j=1;j<=n;j++) ans2[i][j]=tot[i][j]; } return; } else for (int i=0;i<2;i++) { s[x]=i; //枚举状态 dfs(x+1); } } int main() { scanf("%d%d",&m,&n); getchar(); ans1=INF; for (int i=1;i<=m;i++) for (int j=1;j<=n;j++) scanf("%d",&b[i][j]); dfs(1); if (ans1!=INF) { for (int i=1;i<=m;i++) for (int j=1;j<=n;j++) { printf("%d",ans2[i][j]); if (j==n) putchar(' '); else putchar(' '); } } else printf("IMPOSSIBLE "); return 0; }