您的位置:首页 > 其它

JZOJ 3596. 【CQOI2014】和谐矩阵

2018-01-06 20:33 267 查看

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
,b
,f

,ans

;
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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: