POJ3279 Fliptile【狀態壓縮+DFS】

Enjoy_process發表於2018-10-16

Fliptile

Time Limit: 2000MS   Memory Limit: 65536K
Total Submissions: 17511   Accepted: 6409

Description

Farmer John knows that an intellectually satisfied cow is a happy cow who will give more milk. He has arranged a brainy activity for cows in which they manipulate an M × N grid (1 ≤ M ≤ 15; 1 ≤ N ≤ 15) of square tiles, each of which is colored black on one side and white on the other side.

As one would guess, when a single white tile is flipped, it changes to black; when a single black tile is flipped, it changes to white. The cows are rewarded when they flip the tiles so that each tile has the white side face up. However, the cows have rather large hooves and when they try to flip a certain tile, they also flip all the adjacent tiles (tiles that share a full edge with the flipped tile). Since the flips are tiring, the cows want to minimize the number of flips they have to make.

Help the cows determine the minimum number of flips required, and the locations to flip to achieve that minimum. If there are multiple ways to achieve the task with the minimum amount of flips, return the one with the least lexicographical ordering in the output when considered as a string. If the task is impossible, print one line with the word "IMPOSSIBLE".

Input

Line 1: Two space-separated integers: M and N 
Lines 2..M+1: Line i+1 describes the colors (left to right) of row i of the grid with N space-separated integers which are 1 for black and 0 for white

Output

Lines 1..M: Each line contains N space-separated integers, each specifying how many times to flip that particular location.

Sample Input

4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1

Sample Output

0 0 0 0
1 0 0 1
1 0 0 1
0 0 0 0

Source

USACO 2007 Open Silver

問題連結:POJ3279 Fliptile

問題描述:給定M * N 個格子,每個格子可以翻轉正反面,一面是黑色,另一面是白色。黑色翻轉之後變成白色,白色翻轉之後則變成黑色。現在要將所有的格子翻轉為白色。不過每次翻轉一個格子,與它上下左右相鄰接的格子也會被翻轉。求用最少的步驟完成任務時,每個格子的翻轉次數。當有多個解時,輸出字典序最小的一組;解不存在的話,則輸出IMPOSSIBLE。

解題思路:一個格子反轉偶數次和反轉0次一樣,反轉奇數次和反轉1次一樣,因此最後的答案是一個01陣列,即要麼反轉一次,要麼不反轉。如果窮舉所有的結果會超時,現在考慮當第一行的狀態固定時,格子(i,j)的狀態只受格子(i+1,j)控制了,當格子(i,j)不為白色時,格子(i+1,j)就必須反轉,當第二行的狀態確定後,繼續確定第三行,直到倒數第二行決定後,至此1-(m-1)行全為白色了,現在檢視最後一行,如果最後一行都為白色,則是一種可行解。第一行共有2^n中狀態,遍歷各種狀態求出最優解即可。

AC的C++程式:

#include<iostream>
#include<cstring>

using namespace std;
const int N=20;
int m,n;//行列

int g[N][N];//儲存初始情況
int ans[N][N];//儲存最優解
int flip[N][N];//儲存中間解

//查詢(x,y)位置的顏色 奇數為1,偶數為0 ,顏色有初始顏色g[x][y]和上下左右和自己是否反轉共同決定 
//程式下標從1開始儲存,因此可以不用進行下標越界查詢 
int get(int x,int y)
{
	return (g[x][y]+flip[x][y]+flip[x-1][y]+flip[x+1][y]+flip[x][y-1]+flip[x][y+1])%2;
} 

//求出在第一行確定的情況下最少的操作次數,如果無解返回-1
int calc()
{
	//求出從第2行開始的反轉方法
	int count=0;
	for(int i=2;i<=m;i++)
	  for(int j=1;j<=n;j++)
	    if(get(i-1,j))//如果(i-1,j)是黑色的話,則這個格子必須反轉 
		  flip[i][j]=1;
	//判斷最後一行是否為全為0
	for(int j=1;j<=n;j++) 
	  if(get(m,j))
	    return -1;
	//如果是可行解,返回反轉的次數 
	for(int i=1;i<=m;i++)
	  for(int j=1;j<=n;j++)
	    count+=flip[i][j];
	return count; 
}

void solve()
{
	int res=-1;
	/*按照字典序嘗試第一行的所有可能情況,使用狀態壓縮
	  第一行共n個格子,每個格子都有反轉和不反轉兩種選擇,因此共2^n種選擇
	  狀態壓縮:1表示反轉,0表示不反轉
	  舉例說明:當n=2時有兩個格子,共4中選澤
	  00 01 10 11(低位表示序號小的格子)分別表示都不反轉,反轉第一個,反轉第二個,兩個都反轉
	  四種狀態的十進位制數分別為0 1 2 3 。1<<(j-1)表示此狀態下第j個格子是否被反轉,即第j為是否為1	
	  那麼((1<<(j-1))&i)==1表示第j個格子被反轉了因此flip[1][j]=1 */ 
	for(int i=0;i<(1<<n);i++){
		memset(flip,0,sizeof(flip));
		for(int j=1;j<=n;j++) 
		  if((1<<(j-1))&i)
		    flip[1][j]=1;
		int count=calc();//統計這種情況下需要反轉的次數
		if(count>=0&&(res<0||res>count)){//更新最優結果 
			res=count;
			memcpy(ans,flip,sizeof(flip));
		} 
	}
	if(res<0)//無解 
	  printf("IMPOSSIBLE\n"); 
	else{//輸出結果 
		for(int i=1;i<=m;i++)
		  for(int j=1;j<=n;j++)
		    printf("%d%c",ans[i][j],j==n?'\n':' ');
	}
} 

int main()
{
	scanf("%d%d",&m,&n);
	for(int i=1;i<=m;i++)
	  for(int j=1;j<=n;j++)
	    scanf("%d",&g[i][j]);
	solve();
	return 0;
}

 

相關文章