CF1404E

考虑每个 # 的位置使用一块木板,之后进行一些调整。

对于每次调整,我们合并两块木板,使得合并完后木板依旧合法。

这个合并的过程考虑使用二分图的最大独立集。

具体的,我们把相邻的两个 # 之间连边,把边看做新图上的点。对于新图上的点 $u1$(假设它在原图上的对应边为 $l1$),它在新图上会和一个点 $u2$ 连边当且仅当 $u2$ 在原图上对应的边与 $l1$ 有交点。会发现这样的限制组成了一张二分图——原图中上下相连的边只会与左右相连的边建边。跑二分图的最大独立集即可($\text{二分图的最大独立集}=n-\text{最大匹配}$)。

#include <bits/stdc++.h>
#define FL(i, a, b) for(int i = (a); i <= (b); i++)
#define FR(i, a, b) for(int i = (a); i >= (b); i--)
using namespace std;
typedef long long ll;
const int S = 210, N = 80010, M = 320010;
const ll INF = 1e18;
struct E{int v; ll w, nxt;} e[M << 1];
int n, m, s, t, cnt, tot, tote, head[N], now[N], dep[N], e1[S][S], e2[S][S];
char mp[S][S];
void Adde(int u, int v, ll w){
    e[tote] = {v, w, head[u]}, head[u] = tote++;
}
int bfs(){
    queue<int> q; q.push(s);
    memset(dep, 0, sizeof(dep)), dep[s] = 1;
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = now[u] = head[u]; ~i; i = e[i].nxt)
            if(e[i].w && !dep[e[i].v])
                q.push(e[i].v), dep[e[i].v] = dep[u] + 1;
    }
    return dep[t];
}
ll dfs(int u, ll in){
    if(u == t) return in; ll out = 0;
    for(int i = now[u]; ~i && in; i = e[i].nxt){
        int v = e[i].v; ll w = e[i].w; now[u] = i;
        if(dep[v] == dep[u] + 1 && w){
            ll flow = dfs(v, min(in, (ll)w));
            in -= flow, out += flow;
            e[i].w -= flow, e[i ^ 1].w += flow;
        }
    }
    if(!out) dep[u] = 0; return out;
}
ll dinic(){
    ll ret = 0;
    while(bfs()) ret += dfs(s, INF);
    return ret;
}
int main(){
    memset(head, -1, sizeof(head));
    scanf("%d%d", &n, &m), s = ++tot, t = ++tot;
    FL(i, 1, n) FL(j, 1, m){
        scanf(" %c", &mp[i][j]), cnt += mp[i][j] == '#';
        if(mp[i][j] == '#' && mp[i - 1][j] == '#')
            Adde(s, e1[i][j] = ++tot, 1), Adde(e1[i][j], s, 0);
        if(mp[i][j] == '#' && mp[i][j - 1] == '#')
            Adde(e2[i][j] = ++tot, t, 1), Adde(t, e2[i][j], 0);
    }
    FL(i, 1, n) FL(j, 1, m){
        if(mp[i - 1][j] == '#' && mp[i][j - 1] == '#')
            Adde(e1[i][j], e2[i][j], 1), Adde(e2[i][j], e1[i][j], 0);
        if(mp[i - 1][j] == '#' && mp[i][j + 1] == '#')
            Adde(e1[i][j], e2[i][j + 1], 1), Adde(e2[i][j + 1], e1[i][j], 0);
        if(mp[i + 1][j] == '#' && mp[i][j - 1] == '#')
            Adde(e1[i + 1][j], e2[i][j], 1), Adde(e2[i][j], e1[i + 1][j], 0);
        if(mp[i + 1][j] == '#' && mp[i][j + 1] == '#')
            Adde(e1[i + 1][j], e2[i][j + 1], 1), Adde(e2[i][j + 1], e1[i + 1][j], 0);
    }
    printf("%lld\n", cnt - (tot - 2 - dinic()));
    return 0;
}
posted @ 2023-08-16 09:43  徐子洋  阅读(10)  评论(0)    收藏  举报  来源