CS Academy Round#2 E.Matrix Coloring

题目链接

Round#2 E.Matrix Coloring

题目大意

有一个 (N imes M) 的方格图,开始每个格子是白的,接下来每次操作可以把某一行或某一列全部染成红色或蓝色,这里的染色会覆盖一个格子曾经的颜色。现在给出方格图最终的状态(没有白色格子剩余),求最少需要多少次操作把空白的图染成这个状态,若无解则输出 (-1)

(1leq N,Mleq 3000)

思路

答案的上限为 (N+M) ,下限为 (min{N,M})。操作次数最少 (iff) 没被直接染的行和列的数量尽量多,而每一个格子至少被染过一遍,从而没有被染的要么全是行,要么全是列,这里不妨设是行,由于对于没被直接染的行,其颜色状态直接由列的染色情况决定,是唯一的,所以所有没被染的行它们在最终的图上的状态应是完全一样的。

这样子看来我们只需要贪心地找重复次数最多的状态即可,那么该如何判断一个状态是否有解呢?这里先给出结论:任意一个不染的局面有解 (iff) 原局面有解,充分性显然,必要性的证明:

(1) 被钦定不染的行的状态是完全相同的,说明在原局面的一个解中,若这些行存在没被染的,那么把他们统一改成不染的情况并不会改变最终状态,则该钦定的局面有解,所以我们只需要考虑这些行全部被染的情况。
 
(2) 在这个解中行全部被染,考虑把染行这个操作变成把列按照这些行的最终状态全染一遍,把这个操作放在最开始,然后删去这些行,接下来我们染别的地方时无视这个处理,就当网格图还是全白的,然后将原解中除了染这些行以外的操作套上去,可以发现这样我们依然得到了最终的状态,而这个改进的方案是不染这些行的局面的一个解。
 
综上,原局面有解 (Rightarrow) 该不染的局面有解.

根据这个结论,只要题目存在任意一组解,我们就可以直接拿重复最多的状态计算答案了。找到一组解的方法很容易,每次我们找到一个元素全部相同的行或列,将其删除,一直做下去,若图最后能被删完,则有解。

时间复杂度 (O(NM))

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<vector>
#include<map>
#define rep(i,a,b) for(int i = (a); i <= (b); i++)
#define per(i,b,a) for(int i = (b); i >= (a); i--)
#define N 3030
using namespace std;
vector<int> p[N];
map<vector<int>, int> cnt;
bool visr[N], visc[N];
int n, m, row[N], col[N];
int ans;
void solve(){
    cnt.clear();
    int mx = 0;
    rep(i,0,n-1) mx = max(mx, ++cnt[p[i]]);
    ans = min(ans, n+m-mx);
}
bool able(){
    rep(i,0,n-1) rep(j,0,m-1)
        row[i] += p[i][j], col[j] += p[i][j];
    int r = n, c = m;
    while(r > 0 && c > 0){
        bool flag = false;
        rep(i,0,n-1) if(!visr[i] && (row[i] == 0 || row[i] == c)){
            flag = visr[i] = true, r--;
            rep(j,0,m-1) col[j] -= p[i][j];
        }
        if(flag) continue;
        rep(j,0,m-1) if(!visc[j] && (col[j] == 0 || col[j] == r)){
            flag = visc[j] = true, c--;
            rep(i,0,n-1) row[i] -= p[i][j];
        }
        if(!flag) return false;
    }
    return true;
}
int main(){
    cin>>n>>m;
    char ch = getchar();
    rep(i,0,max(n, m)-1) p[i].resize(max(n, m));
    rep(i,0,n-1){
        rep(j,0,m-1) ch = getchar(), p[i][j] = (ch == 'R');
        ch = getchar();
    }
    if(!able()){
        puts("-1"); return 0;
    }
    ans = n+m;
    solve();
    rep(i,0,n-1) rep(j,i,m-1) swap(p[i][j], p[j][i]);
    swap(n, m);
    solve();
    cout<<ans<<endl;
    return 0;  
}

(p.s.) 官方题解好像有点不严谨,其建图后的结论不大好证,也没法感性理解 (QAQ)

原文地址:https://www.cnblogs.com/Neal-lee/p/14691714.html