最大流最小割合集

hdu 3452 Bonsai

一颗带权的树,求最小割使根和叶不连通
添加汇点,每个叶子向其连权值为inf的边,求根到汇点的最小割

hdu 3491 Thieves

基于点的最小割,将每个点拆分成入和出两点

hdu 3987 Harry Potter and the Forbidden Forest

求最少割边数的最小割集,实际上就是求原图有多少次扩增。
借鉴了别人的方法,建图的时候将原权值变为 A = A*(N+1)+1,那么最后得到的maxFlow = (.....)*(N+1)+B,而B就是扩增次数。

hdu 2435 There is a war

在原有向图中可以添加或修改一条原有的边使其权值为inf。求最大的最小割
分析:
如果是修改原有边,则该边的端点必然在原图的割集中,假设修改的边为e{a, b},则再跑一边最大流maxFlow = min(maxFlow(S, a), maxFlow(b,T))。因为原图的割集互不影响,所以可以枚举找出以每个割集中割点为新汇点的最大流,最后求出两者中的最小值即为解。
若添加新边,如果原图中S和T连通则同上。否则,由于S和T所在部分已经构成了割集,所以解法同上。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
const int MAXN = 105;
const int MAXM = MAXN*MAXN*2;
int n, m;
struct _edge
{
    int v, nxt;
    int w;
    _edge(int vv, int ww, int nnxt):v(vv),w(ww),nxt(nnxt){}
    _edge(){}
}ee[MAXM];
int head[MAXN], cnt, sto[MAXM], n1, n2;
int cur[MAXN], dis[MAXN], gap[MAXN], pre[MAXN], ff[MAXN];
void add(int u, int v, int w)
{
    ee[cnt] = _edge(v,w,head[u]); head[u] = cnt++;
    ee[cnt] = _edge(u,0,head[v]); head[v] = cnt++;
}
int sap(int start,int end,int nodenum)
{
    memset(dis,0,4*MAXN);
    memset(gap,0,4*MAXN);
    memcpy(cur,head,4*MAXN);
    int u=pre[start]=start;
    int maxflow=0,aug=-1;
    gap[0]=nodenum;
    while(dis[start]<nodenum)
    {
        loop:
        for(int &i=cur[u];i!=-1;i=ee[i].nxt)
        {
            int v=ee[i].v;
            if(ee[i].w&&dis[u]==dis[v]+1)
            {
                if(aug==-1||aug>ee[i].w)
                    aug=ee[i].w;
                pre[v]=u;
                u=v;
                if(v==end)
                {
                    maxflow+=aug;
                    for(u=pre[u];v!=start;v=u,u=pre[u])
                    {
                        ee[cur[u]].w-=aug;
                        ee[cur[u]^1].w+=aug;
                    }
                    aug=-1;
                }
                goto loop;
            }
        }
        int mindis=nodenum;
        for(int i=head[u];i!=-1;i=ee[i].nxt)
        {
            int v=ee[i].v;
            if(ee[i].w && mindis>dis[v])
            {
                cur[u]=i;
                mindis=dis[v];
            }
        }
        if((--gap[dis[u]])==0)break;
        gap[dis[u]=mindis+1]++;
        u=pre[u];
    }
    return maxflow;
}
void dfs(int u)
{
    ff[u] = 1;
    ++n1;
    for (int i = head[u]; ~i; i=ee[i].nxt)
    {
        int v = ee[i].v;
        if (!ff[v] && (i&1)==0 &&ee[i].w > 0) dfs(v);
    }
}
void _dfs(int u)
{
    ff[u] = 2;
    ++n2;
    for (int i = head[u]; ~i; i=ee[i].nxt)
    {
        int v = ee[i].v;
        if (!ff[v] && (i&1) == 1 && ee[i^1].w > 0) _dfs(v);
    }
}
void init()
{
    cnt = 0;
    memset(head, -1, sizeof head);
    memset(ff, 0, sizeof ff);
    n1 = n2 = 0;
}
int main()   
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    int t, res, a, b;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d%d", &n, &m);
        init();
        while (m--)
        {
            int u, v, c;
            scanf("%d%d%d", &u, &v, &c);
            add(u, v, c);
        }
        int pp = sap(1, n, n);
        a = 0, b = 0;
        dfs(1); _dfs(n);
        for (int i = 0; i< cnt; ++i) sto[i] = ee[i].w;
        for (int u = 2; u< n; ++u)
        {
            if (ff[u] == 1)
            {
                for (int i = 0; i< cnt; ++i) ee[i].w = sto[i];
                a = max(a, sap(1,u,n1));
            }
            else if (ff[u] == 2)
            {
                for (int i = 0; i< cnt; ++i) ee[i].w = sto[i];
                b = max(b, sap(u,n,n2));
            }
        }
        printf("%d\n", min(a,b)+pp);
    }
    return 0;
}

hdu 3996 Gold Mine(最大闭合权图)

闭合图的概念:在一个图中,我们选取一些点构成集合,记为V,且集合中的出边(即集合中的点的向外连出的弧),所指向的终点(弧头)也在V中,则我们称V为闭合图。最大权闭合图即在所有闭合图中,集合中点的权值之和最大的V,我们称V为最大权闭合图。
构造方法:一个点的权值如果为正,则与S相连,且边权为该点的权值,如果为负,则与T相连,且边权为该点权值的绝对值,原来的边的权值设为正无穷。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <vector>
using namespace std;
typedef __int64 LL;
const int MAXN = 3010;
const int MAXM = MAXN*55*2;
const LL INF = LL(1)<<55;
int n, m;
struct _edge
{
    int v, nxt;
    LL w;
    _edge(int vv, LL ww, int nnxt):v(vv),w(ww),nxt(nnxt){}
    _edge(){}
}ee[MAXM];
int head[MAXN], cnt;
int cur[MAXN], dis[MAXN], gap[MAXN], pre[MAXN];
void add(int u, int v, LL w)
{
    ee[cnt] = _edge(v,w,head[u]); head[u] = cnt++;
    ee[cnt] = _edge(u,0,head[v]); head[v] = cnt++;
}
LL sap(int start,int end,int nodenum)
{
    memset(dis,0,4*MAXN);
    memset(gap,0,4*MAXN);
    memcpy(cur,head,4*MAXN);
    int u=pre[start]=start;
    LL maxflow=0,aug=-1;
    gap[0]=nodenum;
    while(dis[start]<nodenum)
    {
        loop:
        for(int &i=cur[u];i!=-1;i=ee[i].nxt)
        {
            int v=ee[i].v;
            if(ee[i].w&&dis[u]==dis[v]+1)
            {
                if(aug==-1||aug>ee[i].w)
                    aug=ee[i].w;
                pre[v]=u;
                u=v;
                if(v==end)
                {
                    maxflow+=aug;
                    for(u=pre[u];v!=start;v=u,u=pre[u])
                    {
                        ee[cur[u]].w-=aug;
                        ee[cur[u]^1].w+=aug;
                    }
                    aug=-1;
                }
                goto loop;
            }
        }
        int mindis=nodenum;
        for(int i=head[u];i!=-1;i=ee[i].nxt)
        {
            int v=ee[i].v;
            if(ee[i].w && mindis>dis[v])
            {
                cur[u]=i;
                mindis=dis[v];
            }
        }
        if((--gap[dis[u]])==0)break;
        gap[dis[u]=mindis+1]++;
        u=pre[u];
    }
    return maxflow;
}
void init()
{
    cnt = 0;
    memset(head, -1, sizeof head);
}
int main()   
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    int t, cs = 0, nl, ng, ww, ed = 3000;
    LL cost, val, res;
    scanf("%d", &t);
    while (t--)
    {
        res = 0;
        n = 0;
        init();
        printf("Case #%d: ", ++cs);
        scanf("%d", &nl);
        for (int i = 1; i<= nl; ++i)
        {
            scanf("%d", &ng);
            for (int j = 1; j<= ng; ++j)
            {
                int d = (i-1)*25+j, a, b;
                ++n;
                scanf("%I64d%I64d%d", &cost, &val, &ww);
                val = val - cost;
                if (val > 0) add(0, d, val), res+=val;
                else if (val < 0) add(d, ed, -val);
                while (ww--)
                {
                    scanf("%d%d", &a, &b);
                    a = (a-1)*25+b;
                    add(d, a, INF);
                }
            }
        }
        printf("%I64d\n", res-sap(0,ed,2+n));
    }
    return 0;
}

hdu 3870 Catch the Theves(平面图最小割最短路)

将平面图的最小割问题转化为最短路问题
#include <cstdio>
#include <cstring>
#include <queue>
#include <algorithm>
#include <vector>
using namespace std;
typedef __int64 LL;
const int MAXN = 405*405;
const int MAXM = MAXN*4;
int n, m;
struct _edge
{
    int v, nxt;
    int w;
    _edge(int vv, int ww, int nnxt):v(vv),w(ww),nxt(nnxt){}
    _edge(){}
}ee[MAXM];
int head[MAXN], cnt;
int vis[MAXN];
void add(int u, int v, int w)
{
    ee[cnt] = _edge(v,w,head[u]); head[u] = cnt++;
    ee[cnt] = _edge(u,w,head[v]); head[v] = cnt++;
}
void init()
{
    cnt = 0;
    memset(head, -1, sizeof head);
    memset(vis, 0, sizeof vis);
}
struct _node
{
    int u, w;
    _node(int a, int b):u(a),w(b){}
    _node(){}
    bool operator < (const _node & a) const
    {
        return w > a.w;
    }
};
priority_queue<_node> qq;
int solve(int S, int T)
{
    while (!qq.empty()) qq.pop();
    _node tn(S, 0); 
    int v, w, res;
    qq.push(tn);
    while (!qq.empty())
    {
        tn = qq.top(); qq.pop();
        if (vis[tn.u]) continue;
        vis[tn.u] = 1;
        if (tn.u == T)
        {
            res = tn.w;
            break;
        }
        for (int i = head[tn.u]; ~i; i=ee[i].nxt)
        {
            if (vis[v=ee[i].v]) continue;
            qq.push(_node(v, tn.w+ee[i].w));
        }
    }
    return res;
}
int main()   
{
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
#endif
    int t, a, p, S, T, k;
    scanf("%d", &t);
    while (t--)
    {
        scanf("%d", &n);
        S = n*n; T = S+1;
        init();
        for (int i = 0; i< n; ++i)
        for (int j = 0; j< n; ++j)
        {
            scanf("%d", &a);
            p = i*n+j;
            if (j == 0 && i != n-1)
                add(T, p, a);
            if (i == n-1 && j != n-1)
                add(T, p-n, a);
            if (i == 0 && j != n-1)
                add(S, p, a);
            if (j == n-1 && i != n-1)
                add(S, p-1, a);
            if (j > 0 && j < n-1 && i < n-1)
                add(p, p-1, a);
            if (i > 0 && i < n-1 && j < n-1)
                add(p-n, p, a);
        }
        printf("%d\n", solve(S, T));
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值