【題意】
給定長寬的黑白棋棋盤擺滿棋子,每次操作可以反轉一個位置和其上下左右共五個位置的棋子的顏色,求要使用最少翻轉次數將所有棋子反轉爲黑色所需翻轉的是哪些棋子。
【題目分析】
這題剛開始被放到搜索的分類下了..然而這和搜索有什麼關係..沒經驗所以想了各種和搜索沾邊的方法,結果沒想出解法,直到看了網上的題解,壓根不是搜索。
具體解法:首先根據題目,每次操作都會影響到周圍的“棋子”,而要使得每個1都被反轉爲0,那麼我們就應當每次都反轉1下方的棋子以改變1爲0.
那麼,當我們處理過1到n-1行的時候,前n-1行就都已經是0了,最後我們只需要檢查最後一行是不是全部爲0就可以檢查這次的出操作是否正確了。如果正確且最小,那就存起來。最後輸出,萬事大吉。
當然,因爲我們要改變第x行的1爲0需要反轉的是x+1行的位置。而這個整個規則是我們驗證一組操作是否能達到目的所用的,那麼我們需要在驗證前先確定操作(沒操作怎麼驗證..)。
於是根據規則內容可知,只需要能確認第一行的翻轉情況,就能夠推出下面所有的翻轉情況並驗證是否正確。於是需要做的就是枚舉第一行的情況了。
【算法流程】
整個代碼分了四部分,處理輸入部分沒啥可說的,接下來就是doWork幹活部分了..
需要乾的活就是枚舉第一行的所有情況然後對於每次枚舉都計算驗證是否符合要求以及反轉所需的次數。其中,第一行的狀態數量可以使用左移運算優化(效率比pow高),於是總共枚舉的數量就有1<<col次。另外,因爲這使得一行的狀態是由一個數字保存的,所以依然使用位運算取得是否翻轉的狀態。
將枚舉好的一次首行狀態存好後就可以交給calc計算和驗證了。驗證就是通過上述翻轉規則操作。其中get(x,y)爲取得某個位置是否爲1的狀態的方法(過程)。
最後檢查結果就好。下面是代碼。
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <cstring>
#include <queue>
#define each(i,n) (int i=1;i<=(n);++i)
#define INF 0x3f3f3f3f
using namespace std;
int row,col;
int arr[20][20];
int flip[20][20],ans[20][20];
int dir[5][2] = {
0,0, 0,1, 0,-1, -1,0, 1,0
};
int get(int x,int y) {
int c = arr[x+1][y+1];
for(int i = 0;i<5;i++) {
int dx = x + dir[i][0];
int dy = y + dir[i][1];
if(dx >=0 && dx<row && dy>=0 && dy<col) {
c+=flip[dx][dy];
}
}
return c&1; // with flip state, if odd return 1
}
int calc() {
for each(i,row-1) {
for(int j = 0;j<col;j++) {
if(get(i-1,j)) ++flip[i][j];
}
}
for(int i=0;i<col;i++) { // check last line
if(get(row-1,i)) return 0;
}
int cnt = 0;
for (int i=0;i<row;i++) { //統計翻轉的次數
for (int j=0;j<col;j++) {
cnt += flip[i][j];
}
}
return cnt;
}
void doWork() {
int cnt = INF;
for(int i=0;i<(1<<col);i++) { //from 0000 to 1111
memset(flip,0,sizeof(flip));
for(int j=0;j<col;j++) {
flip[0][col-j-1] = (i>>j)&1; //get pos state form binary number
}
int num = calc();
if (num<cnt && num!=0) {
cnt = num;
memcpy(ans,flip,sizeof(flip));
}
}
if (cnt==INF) printf("IMPOSSIBLE\n");
else {
for (int i=0;i<row;i++) {
printf("%d",ans[i][0]);
for each(j,col-1) {
printf(" %d",ans[i][j]);
}
printf("\n");
}
}
}
int main() {
while(~scanf("%d%d",&row,&col)) {
memset(arr,0,sizeof(arr));
for each(i,row) {
for each(j,col) {
scanf("%d",&arr[i][j]);
}
}
doWork();
}
}