[LOJ 6030]「雅禮集訓 2017 Day1」矩陣

[LOJ 6030] 「雅禮集訓 2017 Day1」矩陣

題意

給定一個 \(n\times n\)01 矩陣, 每次操做能夠將一行轉置後賦值給某一列, 問最少幾回操做能讓矩陣全爲 1. 無解輸出 -1.c++

\(n \le 1000\).spa

題解

首先手玩下樣例就能夠發現一個很是蝦皮的明顯性質: 由於操做是賦值而不是取或, 因而必定是先讓某一行都爲 1 而後用這一行去染全部不是全 1 的列.code

對於構造一個全 1 的行, 若是行號爲 \(k\), 那麼顯然是用某一行的第 \(k\) 列上的 1 去染第 \(k\) 行. 若是初始狀態剛好不存在任何一行的第 \(k\) 列上有 1, 那麼咱們能夠把任意一個有 1 的行覆蓋到第 \(k\) 列, 那麼就存在某一行的第 \(k\) 列上是 1 了.blog

這個過程當中咱們發現, 只要初始狀態中有 1 就必定有合法方案.get

那麼咱們只要枚舉行號 \(k\) 欽定它來完成染掉全部列的任務, 而後計算出讓它全 1 的最少步數. 若是存在某一行的第 \(k\) 列是 1 那麼答案直接就是第 \(k\)0 的個數, 不然須要一步讓某一行的第 \(k\) 列是 1, 因而等於 0 的個數 \(+1\).it

而後剩下的就沙雕了, 算一算初始狀態中有多少列不是全 1 就好了.class

因此這題複雜度瓶頸實際上是讀入im

參考代碼

#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;
}

相關文章
相關標籤/搜索