问题:
在一个长方形的 4x4 场地上摆放了 16 个正方形的双面棋子。每个棋子的一面是
白色的,另一面是黑色的。每一轮你可以翻转 3 到 5 块,从而将其上侧的颜色从黑色变为
白色,反之亦然。每轮都会根据以下规则选择要翻转的棋子:
(1)选择 16 个棋子中的任意一个。
(2)将该棋子以及相邻的上、下、左、右的棋子反转。
比如下面左边的棋盘,以右边的字符串来表示,其中 b 表示该棋子黑色朝上,w 表示
该棋子白色朝上。
请设计算法采用最少的步骤使整个棋盘上的棋子全部变白或者变黑。
输入要求:
输入由 4 行组成,每行有 4 个字符“w”或“b”,表示棋盘上棋子的颜色。
输出要求:
在输出一个整数,也就是从某个位置开始使棋盘所有棋子变白或者变黑所需的最小步骤。
如果刚开始就满足条件,则输出 0;如果不可能实现目标,则输出-1。
样例输入:
bwwb
bbwb
bwwb
bwww
样例输出:
4
思路:
二进制蛮力法
代码:
#include<bits/stdc++.h>
using namespace std;
#define n 4
int digits;
int chess[10010][10010];
int work[10010][10010];
void turn(int i, int j)
{
work[i][j] = !work[i][j];
if(i-1 >= 0) work[i-1][j] = !work[i-1][j];
if(j-1 >= 0) work[i][j-1] = !work[i][j-1];
if(i+1 <= n) work[i+1][j] = !work[i+1][j];
if(j+1 <= n) work[i][j+1] = !work[i][j+1];
return;
}
int handle1(int way)
{
int cnt = 0;
int i = 0, j = 0;
for(int k = 0; k < digits; k++)
{
if(way & (1 << k))
{
cnt++;
i = k / n;
j = k % n;
turn(i, j);
}
}
for(int ii = 0; ii < n; ii++)
{
for(int jj = 0; jj < n; jj++)
{
if(work[ii][jj] != 0) return -1;
}
}
return cnt;
}
int handle0(int way)
{
int cnt = 0;
int i = 0, j = 0;
for(int k = 0; k < digits; k++)
{
if((~way) & (1 << k))
{
cnt++;
i = k / n;
j = k % n;
turn(i, j);
}
}
for(int ii = 0; ii < n; ii++)
{
for(int jj = 0; jj < n; jj++)
{
if(work[ii][jj] != 1) return -1;
}
}
return cnt;
}
void back()
{
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
work[i][j] = chess[i][j];
}
}
}
int main()
{
digits = n * n;
for(int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
{
char c;
cin >> c;
chess[i][j] = (c == 'w' ? 0 : 1);
work[i][j] = chess[i][j];
}
}
int least = INT_MAX;
int steps;
for(int way = 0; way < (1 << digits); way++)
{
steps = handle1(way);
if(steps != -1)
{
if(steps < least) least = steps;
}
back();
steps = handle0(way);
if(steps != -1)
{
if(steps < least) least = steps;
}
back();
}
if(least == INT_MAX) least = -1;
cout << least << endl;
return 0;
}
回溯法代码:
#include <iostream>
#include <vector>
using namespace std;
const int maxn = 15;
vector<vector<char>> grid(maxn, vector<char>(maxn, 'b'));
int n;
void change(int x, int y){
int dx[] = {0, 0, -1, 1, 0};
int dy[] = {-1, 1, 0, 0, 0};
for(int i = 0; i < 5; i++){
int nx = x + dx[i];
int ny = y + dy[i];
if(grid[nx][ny] == 'b'){
grid[nx][ny] = 'w';
}
else{
grid[nx][ny] = 'b';
}
}
}
bool dfs(int x, char c){ //迭代加深搜索,每次最多搜索x层
if(x == 0){
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(grid[i][j] != grid[1][1]) return false;
}
}
return true;
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
if(grid[i][j] == c){
change(i, j); //注意:该代码未实现查重
if(dfs(x - 1, c)) return true;
change(i, j); //再change一次完成撤销操作
}
}
}
return false;
}
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
cin >> grid[i][j];
}
}
int cntbtow = 0, cntwtob = 0;
for(int i = 0; i <= 10; i++){
if(dfs(i, 'b')){
cntbtow = i;
break;
}
if(dfs(i, 'w')){
cntwtob = i;
break;
}
}
if(cntbtow || cntwtob)
{
int result;
if(cntbtow && cntwtob) result = min(cntbtow, cntwtob);
else result = max(cntbtow, cntwtob);
cout << result << endl;
return 0;
}
cout << -1 << endl;
return 0;
}