【题目链接】
【题目大意】:
农夫约翰知道聪明的牛产奶多。
于是为了提高牛的智商他准备了例如以下游戏。
有一个M×N 的格子,每一个格子能够翻转正反面,它们一面是黑色,还有一面是白色。黑色的格子翻转后就是白色。白色的格子翻转过来则是黑色。
游戏要做的就是把全部的格子都翻转成白色。
只是由于牛蹄非常大,所以每次翻转一个格子时。与它上下左右相邻接的格子也会被翻转。由于翻格子太麻烦了。所以牛都想通过尽可能少的次数把全部格子都翻成白色。如今给定了每一个格子的颜色。请求出用最小步数完毕时每一个格子翻转的次数。最小步数的解有多个时,输出字典序最小的一组。解不存在的话,则输出IMPOSSIBLE。
【解题思路】:
首先。同一个格子翻转两次的话就会恢复原状,所以多次翻转是多余的。此外。翻转的格子的集合相同的话。其次序是无关紧要的。
因此,总共同拥有2NM种翻转的方法。只是这个解空间太大了,我们须要想出更有效的办法。让我们再回想一下前面的问题。在那道题中,让最左端的牛反转的方法仅仅有1种,于是用直接推断的方法确定就能够了。
相同的方法在这里还行得通吗?最好还是先看看最左上角的格子。
在这里。除了翻转(1,1)之外,翻转(1,2)和(2,1)也能够把这个格子翻转,所以像之前那样直接确定的办法行不通。于是最好还是先指定好最上面一行的翻转方法。此时能够翻转(1,1)的仅仅剩下(2,1)了,所以能够直接推断(2,1)是否须要翻转。类似地(2,1)~(2,N)都能这样推断。如此重复下去就能够确定全部格子的翻转方法。最后(M,1)~(M,N)假设并不是全为白色,就意味着不存在可行的操作方法。像这样。先确定第一行的翻转方式,然后能够非常easy推断这样是否存在解以及解的最小步数是多
少,这样将第一行的全部翻转方式都尝试一次就能求出整个问题的最小步数。这个算法中最上面一行的翻转方式共同拥有2N种,复杂度为O(MN2N)。
代码:// C#ifndef _GLIBCXX_NO_ASSERT#include#endif#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if __cplusplus >= 201103L#include #include #include #include #include #include #include #include #include #endif// C++#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include
#include
b:a #define mem(a,b) memset(a,b,sizeof(a)) typedef long long LL; typedef unsigned long long LLU; typedef double db; const int N=16; const int inf=0x3f3f3f3f; int n,m,t,ans,res,cnt,tmp; char str[N]; bool vis[N]; int mat[N][N];///状态之前 int flip[N][N];///状态中间 int Res[N][N];///状态之后 int dir4[4][2]= {
{1,0},{0,1},{-1,0},{0,-1}}; int dir8[8][2]= { {1,0},{1,1},{0,1},{-1,1},{-1,0},{-1,-1},{0,-1},{1,-1}}; int movv[5][2]= { {1,0},{0,1},{0,0},{-1,0},{0,-1}}; using namespace std; inline LL read() { int c=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){c=c*10+ch-'0';ch=getchar();} return c*f; } bool ok(int dx,int dy) { if(dx>=0&&dx<n&&dy>=0&&dy<m) return true; return false; } void getMat()///输入 { for(int i=0; i<n; ++i) for(int j=0; j<m; ++j) scanf("%d",&mat[i][j]); } int getChange(int x,int y)///推断x,y的颜色 { int c=mat[x][y]; for(int i=0; i<5; ++i) { int dx=x+movv[i][0]; int dy=y+movv[i][1]; if(ok(dx,dy))c+=flip[dx][dy]; } return c&1;///奇数为1。偶数为0 } int calc() { ///求出第二行開始的翻转方法 for(int i=1; i<n; ++i){ for(int j=0; j<m; ++j){ if(getChange(i-1,j)!=0)///是白色翻转 flip[i][j]=1; } } ///推断最后一行是否全白 for(int j=0; j<m; ++j){ if(getChange(n-1,j)!=0) return -1; } int sum=0;///统计翻转次数 for(int i=0; i<n; ++i){ for(int j=0; j<m; ++j){ sum+=flip[i][j]; } } return sum; } void solve() { int res=inf; for(int i=0; i< (1<<m); ++i){///枚举第一行的情况 mem(flip,0); for(int j=0; j<m; j++){ flip[0][m-j-1] = i >> j & 1; } int now=calc(); if(now>=0&&now<res){ res=now; memcpy(Res,flip,sizeof(flip)); } } if(res!=inf){ for(int i=0; i<n; ++i){ for(int j=0; j<m; ++j){ printf("%d%c",Res[i][j],j==m-1?'\n':' '); } } } else puts("IMPOSSIBLE"); } int main() { while(cin>>n>>m){ getMat(); solve(); } return 0; } </span>