[LOJ 6030]「雅礼集训 2017 Day1」矩阵

[LOJ 6030] 「雅礼集训 2017 Day1」矩阵

题意

给定一个 \(n\times n\)01 矩阵, 每次操作可以将一行转置后赋值给某一列, 问最少几次操作能让矩阵全为 1. 无解输出 -1.

\(n \le 1000\).

题解

首先手玩下样例就可以发现一个非常虾皮的明显性质: 因为操作是赋值而不是取或, 于是一定是先让某一行都为 1 然后用这一行去染所有不是全 1 的列.

对于构造一个全 1 的行, 如果行号为 \(k\), 那么显然是用某一行的第 \(k\) 列上的 1 去染第 \(k\) 行. 如果初始状态恰好不存在任何一行的第 \(k\) 列上有 1, 那么我们可以把任意一个有 1 的行覆盖到第 \(k\) 列, 那么就存在某一行的第 \(k\) 列上是 1 了.

这个过程中我们发现, 只要初始状态中有 1 就一定有合法方案.

那么我们只要枚举行号 \(k\) 钦定它来完成染掉所有列的任务, 然后计算出让它全 1 的最少步数. 如果存在某一行的第 \(k\) 列是 1 那么答案直接就是第 \(k\)0 的个数, 否则需要一步让某一行的第 \(k\) 列是 1, 于是等于 0 的个数 \(+1\).

然后剩下的就沙雕了, 算一算初始状态中有多少列不是全 1 就行了.

所以这题复杂度瓶颈其实是读入

参考代码

#include <bits/stdc++.h>

namespace rvalue{
    const int MAXN=1010;

    int n;
    int cntx[MAXN];
    int cnty[MAXN];
    char a[MAXN][MAXN];

    int main(){
        scanf("%d",&n);
        bool valid=false;
        for(int i=1;i<=n;i++){
            scanf("%s",a[i]+1);
            for(int j=1;j<=n;j++){
                if(a[i][j]=='#'){
                    valid=true;
                    ++cntx[i];
                    ++cnty[j];
                }
            }
        }
        if(!valid)
            puts("-1");
        else{
            int ans=n;
            for(int i=1;i<=n;i++)
                if(cnty[i])
                    ans=std::min(ans,n-cntx[i]);
                else
                    ans=std::min(ans,n-cntx[i]+1);
            for(int i=1;i<=n;i++)
                if(cnty[i]!=n)
                    ++ans;
            printf("%d\n",ans);
        }
        return 0;
    }
}

int main(){
    rvalue::main();
    return 0;
}

o_67448279_p0.png

转载于:https://www.cnblogs.com/rvalue/p/10603044.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值