【Codeforces Round #639 (Div. 2) D】Monopole Magnets

题目链接

点我呀

翻译

给你一个 \(n\times m\) 的网格, 让你在上面放南极和北极磁铁, 但是这两种磁铁, 在你没有激活他们的时候, 是不会

互相吸引的, 只有在你选中其中一对南北极之后, 北极才会被南极吸引, 然后南极不动, 北极离它近一点 (当然, 只有他们俩

在同一行或者同一列的时候才可以互相吸引, 一个格子可以放多个南北极, 也可以通过吸引, 重叠在一起。

然后现在每个格子被染上色, 黑色表示 一定要有一个 北极可以通过若干个吸引操作到达这个格子, 白色的话, 表示这个格子,

不能有任何一个 北极可以通过若干个吸引操作到达这个格子。

并且, 要求每一行每一列都至少有一个南极。

注意南极是不会动的, 只有北极会动。

让你找出放南北极的方案中, 北极的个数最少的那种, 只要输出对应方案的北极个数就好。

题解

这个题, 我拿到手, 就感觉是那种思维性, 想出来一个构造什么的。

但是我没想出来 \(QAQ\)

然后就去翻题解。

做法是, 首先, 每一行中不可能有多个连续的黑色块的。Why?

因为如果有多个连续黑色块的话, 因为每一行都有一个南极, 所以肯定会出现一个北极在其中一个黑色块,

然后被吸引到另外一个黑色块的过程 (或者南极直接更蠢, 在两个连续的黑色块中间, 也就是在白色块区域)。

这显然都会到白色方块中去, 所以肯定是非法的。违反了 Rule1。

因此每一行中, 只可能有一个连续的黑色块, 或者干脆全是白色块, 这就是非法情况了呀! 赶紧写代码排除掉吧。


Btw,对于一行中全是白色块的情况, 我们这一行肯定是要放一个南极的!

那么我们可以随意放吗? Absolutely no!

假设我们随意放在这一行的第 j 列 (注意啊 这格是白色的!), 然后这一列还有其他的黑色块 (肯定有北极最后能吸引

到这个黑色块位置), 这不就又把北极吸引过来了吗?你个大傻帽。

所以对于一整行全是白色块的情况, 这一行的南极只能放在某一列, 且这一列也全是白色块。

这样我们就能构造出来一个解了: 对于每一行每一列,给南极找一个地方放着就行, 黑色的方块全放上南极就行,

然后全白 (行或列) 的话, 注意放到全是白的列或者行上。

最后看看是不是每行每列都能放个南极就好。

然后在每个黑色的联通块放一个北极 (随便放哪都行, 因为可以和南极重叠在一起啊!)。就是答案了, 因为不要求输出方案, 所以你只要统计下黑色联通块个数就行。

大家都是补到 D 题的人了,求联通块个数你总会求吧?。。

代码

#include<bits/stdc++.h>
#define ll long long
#define rei(x) scanf("%d",&x)
#define rel(x) scanf("%I64d",&x)
#define rep1(i,a,b) for (int i = a;i <= b;i++)
#define rep2(i,a,b) for (int i = a;i >= b;i--)
using namespace std;

const int N = 1e3;
const int MOD = 998244353;

const int dx[4] = {0, 0, 1, -1};
const int dy[4] = {1, -1, 0, 0};
int n, m;
bool allWhiteHang[N + 10], allWhiteLie[N + 10];
bool hang[N+10], lie[N+10];
char s[N + 10][N + 10];
bool bo[N+10][N+10];

void dfs(int x,int y){
    if (!(x >= 1 && x <= n && y >=1 && y <= m)){
        return;
    }
    if (s[x][y] != '#'){
        return;
    }
    if (bo[x][y]) return;
    bo[x][y]  = true;
    for (int i = 0; i < 4; i++){
        int tx = x + dx[i], ty = y + dy[i];
        dfs(tx, ty);
    }
}

int main(){
    #ifdef LOCAL_DEFINE
        freopen("D:\\rush.txt","r",stdin);
    #endif
    ios::sync_with_stdio(0),cin.tie(0);
    cin >> n >> m;
    for (int i = 1;i <= n; i++){
        cin >> (s[i] + 1);
    }

    //每行不能有超过两个连续块。
    for (int i = 1;i <= n; i++){
        int pre = -1, cnt = 0;
        for (int j = 1;j <= m; j++)
            if (s[i][j] == '#'){
                if (j != pre + 1){
                    cnt++;
                }
                pre = j;
            }
        if (cnt > 1){
            cout << -1 << endl;
            return 0;
        }
        if (cnt == 0){
            allWhiteHang[i] = true;
        }
    }

    //每列不能有有两个以上的黑色连续块
    for (int j = 1;j <= m; j++){
        int pre = -1, cnt = 0;
        for (int i = 1;i <= n; i++)
            if (s[i][j] == '#'){
                if (i != pre + 1){
                    cnt++;
                }
                pre = i;
            }
        if (cnt > 1){
            cout << -1 << endl;
            return 0;
        }
        if (cnt == 0){
            allWhiteLie[j] = true;
        }
    }

    //每行都要有一个南极。
    for (int i = 1;i <= n; i++){
        bool ok = false;
        for (int j = 1;j <= m; j++){
            if ( allWhiteHang[i] && allWhiteLie[j] || s[i][j] == '#'){
                lie[j] = 1;
                ok = true;
            }
        }
        if (!ok){
            cout << -1 << endl;
            return 0;
        }
    }
    for (int j = 1;j <= m; j++){
        if (!lie[j]){
            cout << -1 << endl;
            return 0;
        }
    }

    int ans = 0;
    for (int i = 1;i <= n; i++){
        for (int j = 1;j <= m; j++){
            if (!bo[i][j] && s[i][j] == '#'){
                dfs(i,j);
                ans++;
            }
        }
    }
    cout << ans << endl;
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/AWCXV/p/13170673.html