从今天开始把做过的题记录下来。
Flip Game
从翻纸牌的规则可以看出:翻转第i+1行第j列的纸牌,会改变第i 行第j列的纸牌状态,但不影响第i行的其它纸牌。所以,通过翻转下一行的某些纸牌,能达到使上一行纸牌全黑(或全白)的目的。翻完最后一行时,检验最后一行是否全黑(或全白),即可决定此次翻转是否合格且能作为备选方案。
枚举第0行的所有操作,每张纸牌都可能“翻转”或“不翻转”。
process 函数找出将纸牌做成全白(或全黑)的所有操作可能,并返回最小操作数。k[0]到k[3]表示:第0行第0到3列的操作(“翻”或“不翻”)。对某一第0行可能的状态,后面几行的操作就遵循使上一行变成全白(或全黑)的规则,直到最后一行。如果最后一行的状态满足要求,则把这次的翻转次数作为一个候选者。枚举完所有第0行的可能状态,就得到最小翻转次数。
flip 函数进行了翻转(i,j)位置纸牌的操作。布尔值valid 表示最后一行是否满足要求。
#include <cstdio>
#include <iostream>
#include <cmath>
#include <climits>
#include <cstring>
using namespace std;
const int dim = 4;
void flip(int aux[][dim], int i, int j) {
aux[i][j] = !aux[i][j];
if (i > 0) aux[i-1][j] = !aux[i-1][j];
if (i < dim-1) aux[i+1][j] = !aux[i+1][j];
if (j > 0) aux[i][j-1] = !aux[i][j-1];
if (j < dim-1) aux[i][j+1] = !aux[i][j+1];
}
int process(char board[][dim+1], int aux[][dim], bool getWhite) {
int k[4];
int cnt = 0;
int minCnt = INT_MAX;
for (k[0] = 0; k[0] <= 1; ++k[0])
for (k[1] = 0; k[1] <= 1; ++k[1])
for (k[2] = 0; k[2] <= 1; ++k[2])
for (k[3] = 0; k[3] <= 1; ++k[3]) {
cnt = 0;
for (int i = 0; i < dim; ++i)
for (int j = 0; j < dim; ++j)
aux[i][j] = (board[i][j] == 'w');
for (int j=0; j<dim; ++j) {
if (k[j]) {
flip(aux,0,j);
++cnt;
}
}
for (int i=1; i<dim; ++i) {
for (int j=0; j<dim; ++j) {
if (getWhite && !aux[i-1][j]) {
flip(aux,i,j);
++cnt;
}
if (!getWhite && aux[i-1][j]) {
flip(aux,i,j);
++cnt;
}
}
}
bool valid = true;
for (int j=0; j<dim; ++j) {
if (getWhite && !aux[dim-1][j]) valid = false;
if (!getWhite && aux[dim-1][j]) valid = false;
}
if (valid) minCnt = min(minCnt, cnt);
}
return minCnt;
}
int main() {
char board[dim][dim+1];
int aux[dim][dim];
for (int i = 0; i < dim; ++i) {
cin.getline(board[i], dim+1);
}
bool white = true;
int cnt1 = process(board, aux, white);
int cnt2 = process(board, aux, !white);
if (min(cnt1,cnt2) == INT_MAX) printf("Impossible\n");
else printf("%d\n", min(cnt1,cnt2));
return 0;
}