bzoj 4883(带环外向树森林)

来源:互联网 发布:代办软件著作权 编辑:程序博客网 时间:2024/06/04 19:27

题意:在一个n * m的棋盘上要放置若干个守卫。对于n行来说,每行必须恰好放置一个横向守卫;同理对于m列来说,每列必须恰好放置一个纵向守卫。每个位置放置守卫的代价是不一样的,且每个位置最多只能放置一个守卫,一个守卫不能同时兼顾行列的防御。请计算控制整个棋盘的最小代价。n * m<=100000

思路:很容易想到费用流,把(i,j)这个点看作是i和j两个点之间有一条费用为w[i][j]的边,进来总的流量为n+m,出去总的流量为n+m。然后其实,等价于,用n+m条边控制n+m个点的最小费用,就是这么个类kruskal的算法了。外向树上加一个环。

#include <bits/stdc++.h>using namespace std;const int maxn = 100000 + 5;struct Edge{    int u, v, cost;    bool operator < (const Edge &other)const    {        return cost < other.cost;    }}edges[maxn];int loop[maxn], fa[maxn];int uf_find(int x){return x == fa[x] ? x : fa[x] = uf_find(fa[x]);}long long kruskal(int vs, int es){    for(int i = 1; i < vs; i++) fa[i] = i;    sort(edges, edges + es);    long long ans = 0;    for(int i = 0; i < es; i++)    {        Edge e = edges[i];        int fx = uf_find(e.u), fy = uf_find(e.v);        if(loop[fx] && loop[fy]) continue;        if(fx != fy)        {            fa[fx] = fy;            loop[fy] |= loop[fx];        }        else loop[fx] = 1;        ans += e.cost;    }    return ans;}int main(){    int n, m, es = 0;    scanf("%d%d", &n, &m);    for(int i = 1; i <= n; i++)    {        for(int j = 1; j <= m; j++)        {            int x;            scanf("%d", &x);            edges[es++] = {i, j + n, x};        }    }    printf("%lld\n", kruskal(n + m, es));    return 0;}