最小割模型——最大权闭合子图 【NOI2006】bzoj1497 最大获利

定理:最小割=最大流
最大权闭合子图:
有一些点,每一个点有一个权值,可正可负。对于某个点i,如果你选择了i,就必须选择j。现在让你选择一些点,满足上述条件,并且是权值最大。

题目大意:
建立中转站i需要费用pi,如果建立了中转站ai和bi就可以获得收益ci,求最大收益。

题目分析:
转化成最大权闭合子图的模型:
我们把ci具体化为一排节点,则每个点都具有ci的正权值;
另一派点代表中转站,每个点具有-pi的权值;
如果选择ci就必须选择ai和bi。

具体做法:
从源点向每个收益节点连一条容量为ci的边;
从每个中转站节点向汇点连一条容量为pi的边;
从ci向ai和bi分别连一条容量为INF的边;
求该图的最小割,即最大流。
最后用总收益减去最大流。

注意事项:
1、邻接表添边写接口;
2、节点编号不要编重;
3、给源点和汇点找一个好位置。
代码如下:

#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#define N 444444
#define M 444444
using namespace std;
const int lar=0x3fffffff;
int n,m,x,y,z,s,e,ans;
int fir[N],nes[M],v[M],q[M],top=1,d[N];
void edge(int x,int y,int z)
{
    top++;
    v[top]=y;q[top]=z;
    nes[top]=fir[x];fir[x]=top;
}
bool bfs()
{
    static int dl[N];
    memset(d,0,sizeof(d));
    int l=1,r=1;
    dl[1]=s;d[s]=1;
    while(l<=r)
    {
        int c=dl[l++];
        for(int t=fir[c];t;t=nes[t])
        {
            if(d[v[t]] || !q[t]) continue;
            dl[++r]=v[t];
            d[v[t]]=d[c]+1;
            if(v[t]==e) return true;
        }
    }
    return false;
}
int dfs(int c,int flow)
{
    if(c==e || flow==0) return flow;
    int ans=0;
    for(int t=fir[c];t;t=nes[t])
    {
        if(d[v[t]]!=d[c]+1 || !q[t]) continue;
        int f=dfs(v[t],min(flow,q[t]));
        ans+=f;flow-=f;
        q[t]-=f; q[t^1]+=f;
        if(flow==0) break;
    }
    if(!ans) d[c]=-1;
    return ans;
}
int dinic()
{
    int ans=0;
    while(bfs()) ans+=dfs(s,lar);
    return ans;
}
int main()
{
    scanf("%d%d",&n,&m);
    s=0;e=M-1;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        edge(i,e,x);
        edge(e,i,0);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        ans+=z;
        edge(i+n,x,lar);edge(x,i+n,0);
        edge(i+n,y,lar);edge(y,i+n,0);
        edge(s,i+n,z);edge(i+n,s,0);
    }
    ans-=dinic();
    printf("%d\n",ans);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值