关于魔板题解
焦祺 08-12-3
Problem Description
有这样一种魔板:它是一个长方形的面板,被划分成n行m列的n*m个方格。每个方格内有一个小灯泡,灯泡的状态有两种(亮或暗)。我们可以通过若干操作使魔板从一个状态改变为另一个状态。操作的方式有两种:
(1)任选一行,改变该行中所有灯泡的状态,即亮的变暗、暗的变亮;
(2)任选两列,交换其位置。
当然并不是任意的两种状态都可以通过若干操作来实现互相转化的。
你的任务就是根据给定两个魔板状态,判断两个状态能否互相转化。
Input
包含多组数据。第一行一个整数k,表示有k组数据。
每组数据的第一行两个整数n和m。(0<n,m≤100)
以下的n行描述第一个魔板。每行有m个数字(0或1),中间用空格分隔。若第x行的第y个数字为0,则表示魔板的第x行y列的灯泡为“亮”;否则为“暗”。
然后的n行描述第二个魔板。数据格式同上。任意两组数据间没有空行。
Output
共k行,依次描述每一组数据的结果。
若两个魔板可以相互转化,则输出YES,否则输出NO。(注意:请使用大写字母)
Sample Input
2
3 4
0 1 0 1
1 0 0 1
0 0 0 0
0 1 0 1
1 1 0 0
0 0 0 0
2 2
0 0
0 1
1 1
1 1
Sample Output
YES
NO
题目一拿到手,我就觉得状态可能有点庞大,于是先手算了一下题目的状态量。
题目中,最大的魔板是100*100,每一种魔板可以扩展出A = N+(M-1)!种可能,可以扩展的深度可能为:B = (2^N) * (M-1)! 于是可能出现的最大状态为A^B
A~100!差不多有160位,B~30+160位数,于是A^B约为160*190~30000多位数的状态。(或许算错了~~)我一开始就把它看成是广搜来考虑,所以可能会TLE
后来看了下别人的解题思路才豁然开朗。
【网上解题思路】
这道题是一道典型的深搜题,我们可以比较容易的想到思路,但是我们要想优化算法,就必须进行一些小处理,我们可以枚举每一列作为第一列,再和目标状态比较,把每一个不同的行进行反处理,然后我们就可以把剩下的列进行不同的排列,看能否找到一种符合目标状态的排列,这样一来,我们可以大大的降低搜索深度,对于题目的N和M,就可以承受了。
不过看起来简单,做起来没有那么容易
刚开始写完没有那么自信,提交后不出所料TLE了!
稍作修改之后又变成了WA,这让我失去信心
问了下大牛,说是那排序的方法,这让我眼睛一亮!
但大牛试写了一下也写不下去了。
那个魔板的题我用排序的方法再想了一下,虽然是个好的方法,但0、1数相等的时候不能很好的处理,它们是和其它列相关联的,所以用排序的方法不好解决!
于是我抱着微弱的希望,再次修改我的程序,没想到这次RP大暴发,93MS AC!
这道题也给了我一个启示:
对问题的求解时要注意,在得出解法的时候也要注意想想有没有更优更好的解法,思维要发散一些,不要因为搜索就要用搜索的方法去完成。
AC代码:
#include <iostream>
using namespace std;
struct NODE
{
int node[102][102];
};
NODE temp,start,end;
int main()
{
int i,j,k,t,n,m,top;
while (scanf("%d",&t) != EOF)
{
while (t--)
{
scanf("%d %d",&n,&m);
for (i=0;i<n;i++)
{
for (j=0;j<m;j++)
{
scanf("%d",&start.node[i][j]);
}
}
for (i=0;i<n;i++)
{
for (j=0;j<m;j++)
{
scanf("%d",&end.node[i][j]);
}
}
//--------------------------------------------------
//开始枚举每一列作为第一列再和目标状态比较
top = 0; int row[102]; int j0,j1;int flag[102];
for (j0=0;j0<m;j0++)
{
temp = start;
if (j0!=0)
{
for (i=0;i<n;i++) //枚举每一列作为第一列
{
row[i] = temp.node[i][j0];
temp.node[i][j0] = temp.node[i][0];
temp.node[i][0] = row[i];
}
}
for (i=0;i<n;i++) //目标状态比较
{
if (temp.node[i][0] != end.node[i][0])//把每一个不同的行进行反处理
{
for (j=0;j<m;j++)
{
if (temp.node[i][j] == 0)
{
temp.node[i][j] = 1;
}
else
temp.node[i][j] = 0;
}
}
}
//把剩下的列进行不同的排列,看能否找到一种符合目标状态的排列
memset(flag,0,sizeof(flag));
for (j=1;j<m;j++)
{
for (j1=1;j1<m;j1++)
{
if (flag[j1] == 1)
continue;
for (i=1;i<n;i++)
{
if (temp.node[i][j] != end.node[i][j1])
break;
}
if (i == n)
{
flag[j1] = 1;
break;
}
}
}
for (j=1;j<m;j++)
{
if (flag[j] == 0)
{
break; //没有找到
}
}
if (j == m)
{
printf("YES/n");
break;
}
}
if (j0 == m)
{
printf("NO/n");
}
}
}
return 0;
}