procedure2012
It's not worth it to know you're not worth it!

[关键字]:图论 网络流 最大权闭合图

[题目大意]:有n个地方可以建中转站花费不同,有m个顾客群每个顾客群会利用a,b中转站,收益为c,问最大获利。

//=============================================================================================

[分析]:最大权闭合图的典型例题,据说是NOI第一道网络流的题目,可以先看一看《航空计划》这道题。首先可以看成n+m个点,每个点有一个权值,有些是正(收益)有些是负(花费)。有些点依赖于其他的点,由依赖的点向被依赖的点连边就构成了一个最大权闭合图,然后利用网络流求解。详见胡伯涛的一篇关于最小割的论文。

[代码]:

View Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;

const int INF=0x7fffffff;
const int MAXN=55500;

struct node
{
int y,d,next,op;
}e[MAXN*6];
int first[MAXN];
int h[MAXN],num[MAXN];
int S,T,n,m,tot,sum,ans;

void Add(int x,int y,int d)
{
e[++tot].y=y;
e[tot].d=d;
e[tot].op=tot+1;
e[tot].next=first[x];
first[x]=tot;
e[++tot].y=x;
e[tot].d=0;
e[tot].op=tot-1;
e[tot].next=first[y];
first[y]=tot;
}

void Init()
{
scanf("%d%d",&n,&m);
int x,y,z;
S=0,T=n+m+1;
for (int i=1;i<=n;++i)
{
scanf("%d",&x);
Add(i+m,T,x);
}
for (int i=1;i<=m;++i)
{
scanf("%d%d%d",&x,&y,&z);
Add(S,i,z);
Add(i,x+m,INF);
Add(i,y+m,INF);
sum+=z;
}
n=n+m+2;
}

int Find(int u,int flow)
{
if (u==T) return flow;
int temp=flow,pos=n-1;
for (int i=first[u];i;i=e[i].next)
{
if (h[u]==h[e[i].y]+1 && e[i].d>0)
{
int f=Find(e[i].y,min(e[i].d,temp));
temp-=f;
e[i].d-=f;
e[e[i].op].d+=f;
if (h[S]==n || temp==0) return flow-temp;
}
if (e[i].d>0 && pos>h[e[i].y]) pos=h[e[i].y];
}
if (temp==flow)
{
--num[h[u]];
if (!num[h[u]]) h[S]=n;
else
{
h[u]=pos+1;
++num[h[u]];
}
}
return flow-temp;
}

void Solve()
{
memset(h,0,sizeof(h));
memset(num,0,sizeof(num));
num[0]=n;
while (h[S]<n) ans+=Find(S,INF);
printf("%d\n",sum-ans);
}

int main()
{
Init();
Solve();
return 0;
}



posted on 2012-03-21 08:34  procedure2012  阅读(1380)  评论(0编辑  收藏  举报