Fliptile (POJ - 3279) 搜索

給你一個01矩陣,矩陣大小爲M x N。(1 <= M , N <= 15)
每次操作選擇一個格子,使得該格子與上下左右四個格子的值翻轉。
至少多少次操作可以使得矩陣中所有的值變爲0?
請輸出翻轉方案,若沒有方案,輸出"IMPOSSIBLE” 。

Input
第一行輸入兩個數:M和N。(1 <= M , N <= 15)
接下來M行,每行N個數,其值只爲0或1。

Output
輸出M行,每行N個數。
每個數代表該位置翻轉次數

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

剛看題毫無頭緒,既要反轉次數最少又要字典序最小,暴力枚舉複雜度得2^(m*n),太大了,狀壓也不知道該怎麼做。
仔細觀察可以發現,要想使矩陣全部爲0,那麼每個1都要反轉,而且每個位置最多反轉一次,如果反轉兩次就又回去了做的是無用功。既然每個1都要反轉,那麼對於[i,j]這個位置,如果[i-1,j]爲1的話,[i,j]就必須要反轉,所以上一行的狀態確定了,這一行必須反轉的位置就也確定了,所以第1行的狀態確定了,第2行就確定了,那第3行業確定了…,那麼總的反轉次數就確定了。
所以首先要確定第一行的狀態(第一行不一定全爲0,因爲第二行的時候會把第一行全變爲0),這樣到第最後一行的時候前面全部變爲0了,但最後一行不一定,判斷一下即可。
還有一個條件是反轉的字典序最小,因爲第一行狀態確定,後面所有行的飯莊位置就確定了,所以第一行的反轉狀態字典序最小整個的字典序就最小了,所以從小到大枚舉第一行的反轉狀態,取反轉次數最小的那個。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=100;
int n,m,a[N][N],cnt[N][N],num[N][N]; //cnt保存當前狀態的反轉矩陣,num保存最終的答案矩陣
void reverse(int x,int y)//以x,y爲中心反轉
{
	a[x][y]=1-a[x][y];
	if(x>1) a[x-1][y]=1-a[x-1][y];
	if(y>1) a[x][y-1]=1-a[x][y-1];
	if(y<m) a[x][y+1]=1-a[x][y+1];
	if(x<n) a[x+1][y]=1-a[x+1][y];
}
int dfs(int x,int y)
{
	int t=0;//統計反轉次數
	if((x==n+1))
	{
		bool flag=1;
		for(int j=1;j<=m;j++) if(a[n][j]) flag=0;
		if(cnt[x][y]) reverse(x,y);//將矩陣恢復原樣
		if(flag) return t;
		else return 0x3f3f3f3f;//如果不可行返回0x3f3f3f3f這個狀態的次數就一定不會比答案tmp小,就不會出錯了
	}
	if(a[x-1][y])
	{
		reverse(x,y);
		t++;
		cnt[x][y]=1;
	}
	if(y<m) t+=dfs(x,y+1);
	else t+=dfs(x+1,1);
	if(cnt[x][y]) reverse(x,y);//恢復矩陣
	return t;
}
int tmp=0x3f3f3f3f;
void solve(int i,int t)//正向遍歷第一行,第一次判斷的反轉狀態是0000,然後遞歸回m,這是的狀態是0001,再遞歸回去就是0010,然後0011...就實現了從小到大枚舉第一行的狀態
{
	if(i==m+1)
	{
		for(int j=2;j<=n;j++)//將第二行一下的cnt清空
			for(int k=1;k<=m;k++) cnt[j][k]=0;
		t+=dfs(2,1);//計算第2行以後的反轉次數
		if(t<tmp)
		{
			for(int j=1;j<=n;j++)//保存反轉矩陣
				for(int k=1;k<=m;k++)
					num[j][k]=cnt[j][k];
			tmp=t;
		}
	  	return ;
	}
	solve(i+1,t);
	reverse(1,i);
	cnt[1][i]=1;
	solve(i+1,t+1);
	reverse(1,i);//記得恢復現場
	cnt[1][i]=0;
}
int main()
{
	cin>>n>>m;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=m;j++)
			cin>>a[i][j];
	solve(1,0);
	if(tmp<0x3f3f3f3f)
	{
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=m;j++)
				cout<<num[i][j]<<' ';
			puts("");
		}
	}
	else puts("IMPOSSIBLE");
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章