【NAIPC2018】Recovery 题解

传送门


    一道毒瘤题,我很快就想出怎么做了,可是调了半天才调对。


    一个很直观的想法就是高斯消元或者网络流建图,但是本蒟蒻太弱了不会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(R0C0) 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值