2020 ICPC Shanghai B - Mine Sweeper II
题目链接:
https://vjudge.net/contest/416227#problem/B
给定扫雷地图A,B,让你改变B中的某些方格,使总共的数字和与A中一致,且改动次数不超过总格数的一半。
我们可以换一个角度理解数字和:
我们可以把相邻的格子(相邻指的是有共同的端点)间连无向边,边上有权值
如果一个雷和一个没有雷的格子相邻,那么权值为1,如果这两个格子都有雷或者都没雷,那权值就为0。对所有的边权求和,结果就是所求的数字和。
这个角度跟场的势能的概念有点接近
从这个角度讲,重要的是相邻的两个格子的属性是否相同,给定一种地图,我们可以构造与其属性完全相反的地图,其数字和不变。
我们要通过改变B中某些格子的属性,使其数字和与A一致,不如构造成A或每个格子与A都相反的地图。
为了说明这种构造满足动次数不超过总格数的一半,需要证明一下
我们把A,B映射成两个2进制数x,y,每一位就代表一个格子有雷还是没有雷,设与A相反的图对应z
定义两个图之间的差异为\(w(a,b)=\sum_{i=0}^{nm-1}[(a\,xor\,b)\,and\,2^i>0]\)
那么\(x\,xor\,z=111...111\)
\(w(x,y)+w(y,z)=\sum_{i=0}^{nm-1}[(x\,xor\,y)\,and\,2^i>0]+\sum_{i=0}^{nm-1}[(z\,xor\,y)\,and\,2^i>0]=\sum_{i=0}^{nm-1}[(z\,xor\,y)\,and\,2^i>0]+[(x\,xor\,y)\,and\,2^i>0]\)
由于\(x,z\)的每一位都是不同的,因此\([(z\,xor\,y)\,and\,2^i>0],[(x\,xor\,y)\,and\,2^i>0]\)有且仅有一个成立
故\(w(x,y)+w(y,z)=nm\)
\(min(w(x,y),w(y,z))\le\frac{nm}{2}\)
所以我们只要构造出A和A的相反图,再和B比较,取差异小的即可
代码:
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
char A[1010][1010];
char B[1010][1010];
char inv_A[1010][1010];
char inv(char ch)
{
if(ch=='.')return 'X';
return '.';
}
int comp(char map1[][1010],char map2[][1010],int n,int m)
{
int ans=0;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
ans+=(map1[i][j]!=map2[i][j]);
}
}
return ans;
}
int main()
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%s",A[i]+1);
}
for(int i=1;i<=n;i++)
{
scanf("%s",B[i]+1);
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)inv_A[i][j]=inv(A[i][j]);
}
int comp1=comp(A,B,n,m);
if(comp1*2<=n*m)
{
for(int i=1;i<=n;i++)
{
printf("%s\n",A[i]+1);
}
}
else
{
for(int i=1;i<=n;i++)
{
printf("%s\n",inv_A[i]+1);
}
}
}