poj 3279 fliptile (翻转棋盘,枚举方法)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_39562952/article/details/82813250

思路:

翻转偶数次跟翻转0次是一样的,奇数次是跟1次是一样的,所以最后的结果中只可能有0,1;

下一行的状态可以通过前二行的的状态得到,因为在求(i,j)是否要翻转的时候,(i-1,j)(i-1,j-1)(i-1,j+1)(i-2)(j)是否翻转都已经知道了,并且要(i-1,j)这个位置是0,那么就可以推出(i,j)这个位置是否要翻转,所以其实问题中只要枚举第一行的所有情况,就能推出所有的情况,并且一共最多只有2^15种;

枚举方法要保证最后的答案是字典序最小的,那么可以用二进制来表示每一位的状态(0----2^n),第i种情况下,第j个位置的值应该是i>>j&1(不明白的可以自己推导一下),如果答案是1那么就说明要进行翻转,如果是0,就不用;

代码如下:

#include <iostream>
#include <string>
#include <vector>
#include <set>
#include <cstring>
#include <climits>
#include <algorithm>
#include <cmath>
#include <queue>
#include <stdio.h>
#define esp 1e-4
using namespace std;
int ini[20][20];
int flip[20][20];
int final_ans[20][20];
int m,n;
int main()
{
	while(cin>>m>>n)
	{
		int ans=1e8;
		int time=0;//要翻转的次数 
		for(int i=1;i<=m;i++)
		{
			for(int j=1;j<=n;j++)
			{
				cin>>ini[i][j];
				
			}
		}
		
		int ok=0;
		for(int i=0;i<(1<<n);i++)
		{
			memset(flip,0,sizeof(flip));
			for(int j=1;j<=n;j++)
			{
				flip[1][j]=i>>(j-1) &1;
				if(flip[1][j])
					time++;
				
			}
			for(int r=2;r<=m;r++)
			{
				for(int c=1;c<=n;c++)
				{
					if((ini[r-1][c]+flip[r-1][c]+flip[r-1][c-1]+flip[r-1][c+1]+flip[r-2][c])&1)
					{
						flip[r][c]=1;
						time++;
					}
					
					
				}
			}
			int flag=1;
			for(int j=1;j<=n;j++)
			{
				if((ini[m][j]+flip[m][j]+flip[m-1][j]+flip[m][j-1]+flip[m][j+1])&1)
				{
					flag=0;
					break;
				}
				
			}
			if(flag && time<ans)
			{
				ok=1;
				ans=time; 
				for(int i=1;i<=m;i++)
				{
					for(int j=1;j<=n;j++)
					{
						final_ans[i][j]=flip[i][j];
					} 
				} 
			}
		}
		if(ok)
		{
			for(int i=1;i<=m;i++)
			{
				cout<<final_ans[i][1];
				for(int j=2;j<=n;j++)
				{
					cout<<" "<<final_ans[i][j];
				}
				cout<<endl;
			}
		}
		else
			cout<<"IMPOSSIBLE\n";
		
	}
	   
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_39562952/article/details/82813250