有一个由按钮组成的矩阵,每行6个,共5行,每个按钮控制一盏灯
按下一个按钮,该按钮(上下左右)的灯都会改变状态,即灭变亮,亮变灭。
现在要使得所有灯熄灭,给出解决方案。
一、输入&&输出
1.输入
第一行是一个正整数,表示需要解决的案例数目
每个案例由5行6列组成,以空格隔开,
0-->灭,1-->亮
2.输出
对每个案例先输出一行"PUZZLE#m",m是该案例的序号
5行6列,空格隔开
0-->需要按下对应按钮
1-->不需要按下
二、分析&&代码
1.分析
角上改变三盏灯
边上改变四盏灯
其它改变五盏灯
按两次等于没按,每个按钮最多按1次
第一行的状态确定的话,其他的也就有不多的几种情况
要灭第一行第i列亮的灯,就要按下第2行第i列的开关
2.代码
#include <stdio.h>
#include <string.h>
int GetBit(char c,int i);
void SetBit(char *c,int i,int v);
void FlipBit(char *c,int i);
void OutputResult(int t,char result[]);
char orilights[5];
char lights[5];//变化中的矩阵的灯的状态
char result[5];
void main()
{
int T;
printf("请输入组数T:\n");
scanf("%d",&T);
for(int t=1;t<=T;t++)
{printf("请输入灯的状态:\n");
for(int i=0;i<5;i++)
{
for(int j=0;j<6;j++)
{
//把原始的灯的矩阵读进来,放到orilights中
int s;
scanf("%d",&s);
//s是第i行第j列的灯的状态,0或1
SetBit(&orilights[i],j,s);
}
}
for(int n=0;n<64;n++)
{ //枚举第一行的情况
//每个n代表第一行的灯的状态
int switchs=n;//switchs代表当前行的灯的状态
//处理每一行的情况
memcpy(lights,orilights,sizeof(orilights));
for (int i=0;i<5;i++)
{ //第i行的开关状态已经确定,就是switchs
// 每一行的开关状态存在result[i]中
result[i]=switchs;
//让第i行的开关起作用,
//首先让它影响第i行的灯。
for(int j=0;j<6;j++)
{
if(GetBit(switchs,j))
{//对同一行的灯进行处理
if(j>0)
FlipBit(&lights[i],j-1);
//灯的状态放在lights中
FlipBit(&lights[i],j);
if(j<5)
FlipBit(&lights[i],j+1);
}
}
if(i<5)
{//处理第i+1行的情况
lights[i+1]^=switchs;
//switchs代表第i行的开关状态
}
switchs=lights[i];
}//确保前四行的开关都是灭的
//最后一行的开关状态放在lights[4]中
if(lights[4]==0)
{
//第5行都是灭的,成功了
OutputResult(t,result);
break;
}
}
}
}
int GetBit(char c,int i)
{ //取字符c的第i个bit
return (c>>i)&1;
//c右移i位,c的第i位就跑到了第0位
}
void SetBit(char *c,int i,int v)
{
//设置c的第i位为v
if(v==1)
{
*c|=(1<<i);//c的第i位变成了1
}
else
*c&=~(1<<i);//(1<<i)是只有第i位为1,其它位都为0
//c第i位为0,其它位不变
}
void FlipBit(char *c,int i)
{
//将c的第i位翻转,1变成0,0变成1
*c^=(1<<i);
}
void OutputResult(int t,char result[])
{
printf("PUZZLE #%d\n",t);
for(int i=0;i<5;i++)
{
for(int j=0;j<6;j++)
{
printf("%d",GetBit(result[i],j));
if(j<5)
printf(" ");
}
printf("\n");
}
}
3.运行结果
请输入组数T:
1
请输入灯的状态:
0 1 1 0 1 0
1 0 0 1 0 0
0 1 1 0 1 1
1 0 0 1 1 1
0 0 0 1 1 0
PUZZLE #1
1 0 0 1 1 1
1 0 0 0 0 0
1 1 0 0 1 1
1 1 0 1 1 1
0 1 0 1 1 0
Press any key to continue
三.memcpy函数
函数原型
void *memcpy(void *destin, void *source, unsigned n);
参数
-
destin-- 指向用于存储复制内容的目标数组,类型强制转换为 void* 指针。
-
source-- 指向要复制的数据源,类型强制转换为 void* 指针。
-
n-- 要被复制的字节数。
返回值
该函数返回一个指向目标存储区destin的指针。
功能
从源source所指的内存地址的起始位置开始拷贝n个字节到目标destin所指的内存地址的起始位置中。 [2]
所需头文件
C语言:#include<string.h>
C++:#include<cstring>
应用说明
1.source和destin所指的内存区域可能重叠,但是如果source和destin所指的内存区域重叠,那么这个函数并不能够确保source所在重叠区域在拷贝之前不被覆盖。而使用memmove可以用来处理重叠区域。函数返回指向destin的指针。
2.如果目标数组destin本身已有数据,执行memcpy()后,将覆盖原有数据(最多覆盖n)。如果要追加数据,则每次执行memcpy后,要将目标数组地址增加到你要追加数据的地址。
注意:source和destin都不一定是数组,任意的可读写的空间均可。
区别
strcpy和memcpy主要有以下3方面的区别。
1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy。