poj3279(dfs+二進制枚舉思路)
題意轉載自https://www.cnblogs.com/blumia/p/poj3279.html
題目屬性:DFS
相關題目:poj3276
題目原文:
【desc】
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".
【In】
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
【Out】
Lines 1.. M: Each line contains N space-separated integers, each specifying how many times to flip that particular location.
【SampIn】
4 4
1 0 0 1
0 1 1 0
0 1 1 0
1 0 0 1
【SampOut】
0 0 0 0
1 0 0 1
1 0 0 1
0 0 0 0
【一句話題意】
給定長寬的黑白棋棋盤擺滿棋子,每次操作可以反轉一個位置和其上下左右共五個位置的棋子的顏色,求要使用最少翻轉次數將所有棋子反轉為黑色所需翻轉的是哪些棋子(這句話好長...)。
【題目分析】
盯著奇怪的題目看了半天發現和奶牛沒什麽卵關系..奶牛智商高了產奶高是什麽鬼說法...
這題剛開始被放到搜索的分類下了..然而這和搜索有什麽關系..沒經驗所以想了各種和搜索沾邊的方法,結果沒想出解法,直到看了網上的題解,壓根不是搜索啊有木有。
然後這題網上給的分類是簡單題..簡單題..蒟蒻跪了..
好了開始說解法。首先根據題目,每次操作都會影響到周圍的“棋子”,而要使得每個1都被反轉為0,那麽我們就應當每次都反轉1下方的棋子以改變1為0.
那麽,當我們處理過1到n-1行的時候,前n-1行就都已經是0了,最後我們只需要檢查最後一行是不是全部為0就可以檢查這次的出操作是否正確了。如果正確且最小,那就存起來。最後輸出,萬事大吉。
當然,因為我們要改變第x行的1為0需要反轉的是x+1行的位置。而這個整個規則是我們驗證一組操作是否能達到目的所用的,那麽我們需要在驗證前先確定操作(沒操作怎麽驗證..)。
於是根據規則內容可知,只需要能確認第一行的翻轉情況,就能夠推出下面所有的翻轉情況並驗證是否正確。於是需要做的就是枚舉第一行的情況了。
【算法流程】
#include<stdio.h>
#include<string.h>
int n,m;
int count,min;
int s[20][20],s1[20][20];
int lg[20][20];
int cg(int a,int b)
{
int i,j;
s[a][b]=!s[a][b];
if(a-1>0)
s[a-1][b]=!s[a-1][b];
if(a+1<=n)
s[a+1][b]=!s[a+1][b];
if(b-1>0)
s[a][b-1]=!s[a][b-1];
if(b+1<=m)
s[a][b+1]=!s[a][b+1];
}
int do1(int a)
{
count=0;
int k=1;
int i,j;
memcpy(s,s1,sizeof(s));
memset(lg,0,sizeof(lg));
for(j=1;j<=m;j++)
{
if((a&(1<<(m-j)))>0) //記得a&1<<(m-j)要打括號,不然會是a&(1<<(m-j)>0)
{
cg(1,j),count++,lg[1][j]=1;
//printf("w%d %d\n",a,a&(1<<(m-j)));
}
}
for(i=1;i<n;i++)
for(j=1;j<=m;j++)
{
if(s[i][j]==1)
cg(i+1,j),count++,lg[i+1][j]=1;
}
for(j=1;j<=m;j++)
if(s[n][j]==1)
k=0;
//if(k==1)
//printf("%d %d\n",count,a);
return k;
}
int main()
{
int i,j,k;
while(scanf("%d%d",&n,&m)!=EOF)
{
min=n*m+1;
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
scanf("%d",&s1[i][j]);
k=-1;
for(i=0;i<(1<<m);i++)//第一行翻動情況
{
if(do1(i)==1&&min>count)
{
min=count;
k=i;
//printf("%d\n",min);
}
}
if(k==-1)
printf("IMPOSSIBLE\n");
else
{
do1(k);
//printf("%d",k);
for(i=1;i<=n;i++)
{
j=1;
printf("%d",lg[i][j]);
for(j=2;j<=m;j++)
printf(" %d",lg[i][j]);
printf("\n");
}
}
}
return 0;
}
poj3279(dfs+二進制枚舉思路)