BZOJ 1497 [NOI2006]最大获利

最大权闭合图

关于最大权闭合图的那套理论,去看胡伯涛论文《最小割模型在信息学竞赛中的应用》吧,orz学长。

顺便做点笔记:

最大权闭合图解决这样一类问题:如果选了一个点, 则这个点所有可达的点都必须选,求最大点权和。

最大权闭合图的建图流程:

1.增加源s汇t
2.源s连接原图的正权点,容量为相应点权
3.原图的负权点连接汇t,容量为相应点权的相反数
4.原图边的容量为正无限.

最终答案就是:正点权和 - 最大流

最终选出的点集就是在残量网络中从S可达的所有点,也就是最小割后S的连通块里除了S的所有点。

套路?按胡学长PPT里的话说就是:在任意带权有向图中,只要有依赖关系需要解决,最大权闭合图都普遍适用。(普适性)

#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#define N 55005
using namespace std;
namespace runzhe2000
{
    const int S = N-1, T = N-2, INF = 1<<29;
    int ecnt = 1, n, m, last[N], cur[N], sum, level[N];
    struct edge{int next, to, flow;}e[N<<3];
    void _addedge(int a, int b, int c){e[++ecnt]=(edge){last[a],b,c}; last[a]=ecnt;}
    void addedge(int a, int b, int c){_addedge(a,b,c);_addedge(b,a,0);}
    bool bfs()
    {
        queue<int> q; q.push(S); memset(level,0,sizeof(level)); level[S] = 1;
        for(;!q.empty();)
        {
            int x = q.front(); q.pop();
            for(int i = last[x]; i; i = e[i].next)
            {
                int y = e[i].to; if(level[y] || !e[i].flow) continue;
                level[y] = level[x] + 1; if(y == T) return 1; q.push(y);
            }
        } return 0;
    }
    int dfs(int x, int flow)
    {
        if(x == T) return flow; int use = 0;
        for(int &i = cur[x]; i; i = e[i].next)
        {
            int y = e[i].to; if(level[x] + 1 != level[y]) continue;
            int w = dfs(y, min(flow-use,e[i].flow));
            e[i].flow -= w; e[i^1].flow += w; use += w;
            if(use == flow) return use;
        } return use;
    }
    int dinic(){int ret = 0; for(; bfs(); ) memcpy(cur, last, sizeof(last)), ret += dfs(S,INF); return ret;}
    void main()
    {
        scanf("%d%d",&n,&m);
        for(int i = 1; i <= n; i++) 
        {
            int p; scanf("%d",&p);
            addedge(i, T, p);
        }
        for(int i = 1; i <= m; i++)
        {
            int a, b, c; scanf("%d%d%d",&a,&b,&c);
            sum += c; addedge(S, i+n, c);
            addedge(i+n, a, INF);
            addedge(i+n, b, INF);
        }
        printf("%d\n",sum - dinic());
    }
}
int main()
{
    runzhe2000::main();
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值