Description
我们称一个由0和1组成的矩阵是和谐的,当且仅当每个元素都有偶数个相邻的1.一个元素相邻的元素包括它本身,及他上下左右的4个元素(如果存在)。
给定矩阵的行数和列数,请计算并输出一个和谐的矩阵。注意:所有元素为0的矩阵是不允许的。
Input
输入一行,包含两个空格分隔的整数m和n,分别表示矩阵的行数和列数。
Output
输出包含m行,每行n个空格分隔整数(0或1),为所求矩阵。测试数据保证有解。
Sample Input
4 4
Sample Output
0 1 0 0
1 1 1 0
0 0 0 1
1 1 0 1
Data Constraint
1<=m,n<=40
Solution
首先,我们可以发现结论①:存在一种方案满足第一行沿中轴(竖着的)左右对称。
因为左边满足条件则右边也会满足条件。
接着,我们又可以发现结论②:一个方案,只要确定了第一行,接下来的每一个位置都唯一确定。
因为我们可以通过上一行位置的1的奇偶性推出下一行。
于是我们就可以先枚举第一行的一半( O(220) ),在翻折得到第一行。
之后再往后推,看最后一行是否合法即可。找到一种方案就可以直接输出了。
但是判断是否合法的时候如果是 O(N2) 判断的话,就可能会超时(主要看人品)。
我们迫切要寻求更快的方法迅速推导!
机智地考虑位运算。
首先,我们先处理出第一行的异或值(即每个数周围的1的奇偶性),
设为 num ,并将其压成一个长整型。
那么我们要推出下一行的话,每一个数就要异或周围的四个数(包括自己)。
于是就可以直接异或起来即可:
num xor (num<<1) xor (num>>1) xor last左移一位、右移一位、自己、上一行(在草稿纸上画一画更好理解)。
这样就处理出每个数异或周围的数之后的值了,还是 O(1) 的!
注意不要忘了抹去左移后突出的一位( and 上 2m−1 这个全 1 值即可)。
最后判断最后一行的异或值是否为零即可,零则说明合法,可以直接输出。
这样判断的时间复杂度就是 O(N) ,跑得飞快(位运算真强大)!
总时间复杂度 O(N∗2n2) ,轻松通过本题。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=42,way[5][2]={{1,0},{0,1},{-1,0},{0,-1},{0,0}};
int n,m,h;
long long an;
int a[N],b[N],f[N][N],ans[N][N];
void dfs(int x)
{
if(x>h)
{
long long num=0,last=0;
memcpy(b,a,sizeof(b));
for(int i=(m&1)?h-1:h,k=h;i;i--) b[++k]=a[i];
for(int i=1;i<=m;i++)
{
num=num<<1|(b[i-1]^b[i]^b[i+1]);
last=last<<1|b[i];
}
for(int i=2;i<=n;i++)
{
long long s=num;
num=last^num^(num<<1&an)^(num>>1);
last=s;
}
if(!num)
{
for(int i=1;i<=m;i++)
if(ans[1][i]=b[i])
for(int j=0;j<=4;j++) f[1+way[j][0]][i+way[j][1]]++;
for(int i=2;i<=n;i++)
for(int j=1;j<=m;j++)
if(f[i-1][j]&1)
{
ans[i][j]=1;
for(int k=0;k<=4;k++) f[i+way[k][0]][j+way[k][1]]++;
}
for(int i=1;i<=n;i++,printf("\n"))
for(int j=1;j<=m;j++)
printf("%d ",ans[i][j]);
exit(0);
}
return;
}
a[x]=1;
dfs(x+1);
a[x]=0;
dfs(x+1);
}
int main()
{
scanf("%d%d",&n,&m);
h=(m+1)>>1,an=((long long)1<<m)-1;
dfs(1);
return 0;
}