算法基础:北京大学
问题描述
Bilibili搬运地址:https://www.bilibili.com/video/av10046345/?p=4
建议如果视频第一遍没看懂,再看一遍就好了~.~
代码及注释
#include <stdio.h>
int puzzle[6][8], press[6][8];//分别在上,左,右添加一行或一列用于处理边界问题
//推测验证过程:
//根据第一行的情况,确定第二行的情况;然后用第二行的情况确定第三行,依次类推
bool guess()
{
int c, r;
//用当前行的状态来确定下一行的状态,
for(r=1; r<5; r++)
{
for(c=1; c<7; c++)
{
//这里有点巧妙,位置(r,c)按一下,一共会影响周边4个点
//如果要使得(r,c)的值为0,那么在press(r,c)+press(r-1,c)+press(r+1,c)+press(r,c-1)+press(r,c+1)的按下的值要和puzzle(r,c)的值共奇偶
//换句话说( press(r,c)+press(r-1,c)+press(r+1,c)+press(r,c-1)+press(r,c+1) )%2 == puzzle(r,c)
//由于仅仅press(r+1,c)未知,因此把上式转换一下即可。
press[r+1][c]=(puzzle[r][c]+press[r][c]+press[r-1][c]+press[r][c-1]+press[r][c+1])%2;
}
}
//判断最后一行是否可用熄灭,如果不能说明第一行按错,需要重新枚举
for(c=1; c<7; c++)
{
if ((press[5][c-1]+press[5][c]+press[5][c+1]+press[4][c])%2 != puzzle[5][c])
{
return false;
}
}
return true;
}
//枚举过程:
//对press第1行的元素press[1][1]~press[1][6]的各种取值进行枚举
//即对第一行各种可能的按键方式都进行枚举,这里用了二进制的思路进行枚举
void enumerate()
{
int c;
for(c=1; c<7; c++)
{
//第一个枚举情况,就是都不按
press[1][c]=0;
}
while(guess()==false)
{
press[1][1]++;
c=1;
//模拟二进制,即从000000->111111
while(press[1][c]>1)
{
//累加进位
press[1][c]=0;
c++;
press[1][c]++;
}
}
return ;
}
int main()
{
int cases, i, r, c;
scanf("%d", &cases);
//初始化置零press
for(r=0; r<6; r++)
{
press[r][0]=press[r][7]=0;
}
for(c=0; c<7; c++)
{
press[0][c]=0;
}
for(i=0; i<cases; i++)
{
//注意这里循环从1开始,就是因为手工添加了一个边缘,要跳过
for(r=1; r<6; r++)
{
for(c=1; c<7; c++)
{
scanf("%d", &puzzle[r][c]); //读入输入数据
}
}
enumerate();
printf("PUZZLE#%d\n", i+1);
for (r=1; r<6; r++) {
for (c=1; c<7; c++) {
printf("%d ", press[r][c]);
}
printf("\n");
}
}
return 0;
}