1. 程式人生 > >poj 3279 fliptile (翻轉棋盤,列舉方法)

poj 3279 fliptile (翻轉棋盤,列舉方法)

思路:

翻轉偶數次跟翻轉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;
}