一道毒瘤题,我很快就想出怎么做了,可是调了半天才调对。
一个很直观的想法就是高斯消元或者网络流建图,但是本蒟蒻太弱了不会QAQ。我们稍加分析一下,肯定是不会出现任意一个矩形四个顶点均为0的情况,因为这样全部赋为1,仍满足条件,而且结果更优。这样我们就可以根据输入的01情况大致判断出每行每列的0的个数了。
对于每一行,如果元素个数为奇数,输入值为0,那么就得有一个0;如果元素个数为偶数,输入值为1,也得有一个0。列同理。我们这时就知道那些行得有0,那些列得有0了。设要有0的行的个数为 R0 R 0 ,要有0的列的个数为 C0 C 0 ,很容易发现,最终0的个数最少为 max(R0,C0) m a x ( R 0 , C 0 ) 。如何判断无解的情况呢?只要 2|abs(R0−C0) 2 | a b s ( R 0 − C 0 ) ,就一定存在解,反之一定没解,因为肯定会把一行或一列的个数的奇偶性搞反。由于要求最终转成的二进制值最小,那么0得尽量靠前。对于 R0 R 0 与 C0 C 0 相差的部分,从小到大先把缺失的行或列号赋为1,因为这样一定靠前,剩下的一一配对就行了,把得到的行列坐标的位置上全赋为0,剩下的全赋为1就是最优解了。
时间复杂度: Θ(rc+r+c) Θ ( r c + r + c ) 。跑得飞快,0ms。(我只会这么做,可能我还是菜吧。)
我的代码:
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<vector>
using namespace std;
int r,c;
char a[51],b[51];
vector<int>x,y;
int v[51][51];
int main()
{
scanf("%s%s",a,b);
r=strlen(a);
c=strlen(b);
for(int i=0;i<r;i++)
{
if(c&1)
{
if(a[i]=='0')x.push_back(i+1);
}
else
{
if(a[i]=='1')x.push_back(i+1);
}
}
for(int i=0;i<c;i++)
{
if(r&1)
{
if(b[i]=='0')y.push_back(i+1);
}
else
{
if(b[i]=='1')y.push_back(i+1);
}
}
int lx=x.size(),ly=y.size();
for(int i=1;i<=r;i++)
{
for(int j=1;j<=c;j++)v[i][j]=1;
}
if(lx<=ly)
{
if((ly-lx)&1)
{
printf("-1");
return 0;
}
for(int i=0;i<ly-lx;i++)v[1][y[i]]=0;
for(int i=ly-lx;i<ly;i++)v[x[i-ly+lx]][y[i]]=0;
}
else
{
if((lx-ly)&1)
{
printf("-1");
return 0;
}
for(int i=0;i<lx-ly;i++)v[x[i]][1]=0;
for(int i=lx-ly;i<lx;i++)v[x[i]][y[i-lx+ly]]=0;
}
for(int i=1;i<=r;i++)
{
for(int j=1;j<=c;j++)printf("%d",v[i][j]);
puts("");
}
return 0;
}