题意:有4*4的正方形,每个格子要么是黑色,要么是白色,当把一个格子的颜色改变(黑->白或者白->黑)时,其周围上下左右(如果存在的话)的格子的颜色也被反转,问至少反转几次可以使4*4的正方形变为纯白或者纯黑?
思路:
做这道题,首先要自己试试几个例子,然后提炼2个要点:
1.对同一个格子翻2次和翻0次的效果是一样的,同理翻3次和翻0次的效果也是一样的
2.对于2个格子,他们翻的顺序不影响结果
3.翻几次 对应于 对几个格子进行翻的操作
所以这题的问题就变成了“十六个格子里选几个格子进行翻转操作可以变成同色棋盘”,要么16选1,要么16选2,一直判断到16选16,总的情况为
看样子不会超时,所以放心大胆地用暴力枚举(dfs)吧,记得从16选0开始判断,依次增到16选16,如果都不满足说明不可能。
#include<iostream>
#include<string>
using namespace std;
int map[6][6],ans,k,limt;
struct A
{
int x,y;
};
A a[16];
void fan(int x,int y) //翻转函数
{
map[x][y]=!map[x][y];
map[x-1][y]=!map[x-1][y];
map[x+1][y]=!map[x+1][y];
map[x][y+1]=!map[x][y+1];
map[x][y-1]=!map[x][y-1];
}
int jude() //判断是否整个棋盘均为同一种颜色的函数
{
int n1,n2,i,j;
int t=map[1][1]; //如果和map[1][1]不相等就返回0,否则返回1
for(i=1;i<=4;i++)
for(j=1;j<=4;j++)
if(map[i][j]!=t)
return 0;
return 1;
}
void dfs(int cur,int d) //cur指当前你转了几个棋子,d指你这次该从哪个棋子开始选
{
if(jude()) {ans=cur;} //如果棋盘达到条件,给ans赋值
if(15-d<limt-cur-1||cur+1>limt) return ; //如果剩余的可选棋子比所需棋子少或者你已经转的棋子比上限大
for(int i=d;i<16&&ans==-1;i++) //从d开始选择该转的棋子。如果答案ans已经得到则退出循环
{
fan(a[i].x,a[i].y); //翻转棋子
dfs(cur+1,i+1); //cur+1,同时i+1,即下一次应该从i+1的棋子开始选,避免重复
fan(a[i].x,a[i].y); //记得翻回来
}
}
int main()
{
char str;
int i,j;
memset(map,0,sizeof(map));
for(i=1;i<=4;i++) //构建棋盘
{
for(j=1;j<=4;j++)
{
scanf("%c",&str);
if(str=='b') map[i][j]=1;
else map[i][j]=0;
}
getchar();
}
k=0;
for(i=1;i<=4;i++) //用a数组放入每个棋子的坐标
{
for(j=1;j<=4;j++)
{
a[k].x=i;
a[k++].y=j;
}
}
ans=-1;
for(i=0;i<=16;i++)
{
limt=i; //limt指上限,即你这次打算翻几个棋子
dfs(0,0);
if(ans!=-1)
{
printf("%d\n",ans);
return 0;
}
}
printf("Impossible\n");
}