D - Fliptile 二進位制列舉+反轉
阿新 • • 發佈:2018-12-25
題目大意:讓牛踩瓦片使得瓦片反轉,但是因為牛蹄太大,在踩到要反轉的瓦片的同時,也使相鄰的上下左右四個瓦片反轉。現在問,牛如何踩瓦片,才能使得踩的次數儘量少的前提下使瓦片全部反轉過來。
思路:仔細想你會發現瓦片反轉是有規律的,當第一行如何反轉確定下來時,第二行會根據第一行的反轉情況確定下來,比如第一行進行反轉處理後 最終狀態是1001,那麼就要通過反轉第二行的瓦片,使得第一行變為0000的狀態,則第二行反轉第一個和第四個。接著一次進行第三行、第四行、一直到n-1行。這裡,為什麼是n-1行呢,由於我們每次的反轉(除第一行外)都是根據上一行的狀態進行的,當反轉完第n-1行時,第n行瓦片的狀態就已經確定了。因此我們可以通過暴力列舉第一行瓦片的反轉,通過判斷最後一行是否符合要求,來判斷第一行的這個列舉情況是否可行。 這裡,列舉用到二進位制列舉。
不懂二進位制列舉的:傳送門
下面上程式碼:
#include<stdio.h> #include<string.h> #include <iostream> using namespace std; int t[30][30], tem[30][30], m[30][30];//t表示瓦片狀態,tem表示是否對該瓦片進行反轉 int M,N,dir[5][2] = { 0,0,1,0,0,1,-1,0,0,-1 }; int get(int x, int y)//判斷該瓦片狀態,從而判斷是否需要對它下面的這個瓦片進行反轉 { int c = t[x][y]; for (int i = 0; i < 5; i++)//由於該瓦片的周圍四個方位對其都有影響,那麼通過c來記錄該瓦片的狀態,若c為偶數,則表示白麵朝上,若c為奇數,則表示黑麵朝上。 { int x1 = x + dir[i][0], y1 = y + dir[i][1]; c += tem[x1][y1]; } return c % 2; } int cal()//遍歷計算反轉瓦片的次數 { for (int i = 2; i <= M; i++) for (int j = 1; j <= N; j++) if (get(i - 1, j) == 1) tem[i][j] = 1; for (int i = 1; i <= N; i++) if (get(M, i))return -1; int res = 0; for (int i = 1; i <= M; i++) for (int j = 1; j <= N; j++) res += tem[i][j]; return res; } int main() { int min = -1; scanf("%d%d", &M, &N); for (int i = 1; i <= M; i++) for (int j = 1; j <= N; j++) scanf("%d", &t[i][j]); for (int i = 0; i < (1 << N); i++) { memset(tem, 0, sizeof(tem)); for (int j = 1; j <= N; j++) tem[1][j] = (i >> (j - 1)) & 1;//二進位制列舉第一行的所有可能 int num = cal(); if (num >= 0 && (min<0 || min>num)) { min = num; memcpy(m, tem, sizeof(tem)); } } if (min == -1)printf("IMPOSSIBLE\n"); else { for (int i = 1; i <= M; i++) for (int j = 1; j <= N; j++) printf("%d%c", m[i][j], j == N ? '\n' : ' '); } }