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

来源:互联网 发布:数据 集合提供商 编辑:程序博客网 时间:2024/05/10 19:22

定理:最小割=最大流
最大权闭合子图:
有一些点,每一个点有一个权值,可正可负。对于某个点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 444444using 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
原创粉丝点击