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