[bzoj1497][NOI2006]最大获利_网络流_最小割

最大获利 bzoj-1497

    题目大意:可以建立一个点,花费一定的代价;将已经建立的两个点之间连边,得到一定收益。有些节点之间是不允许连边的。

    注释:1<=点数<=5,000,1<=允许连边的边数<=50,000。

      想法:将每个可以相连的点之间的边在网络流里建立成一个节点,将源点连这条边的收获,向这两条边的端点分别连两条inf的边,所有的端点向源点连边权为该点代价的边。然后由最大流等于最小割,求最大流即可。

    最后,附上丑陋的代码... ...

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
#define inf 0x3f3f3f3f
using namespace std;
int head[400010],to[400010],nxt[400010],tot=1;
int dis[400100];
int n,m;
// int p[10010];
int f[400010];
inline void add(int x,int y,int z)
{
	to[++tot]=y;
	f[tot]=z;
	nxt[tot]=head[x];
	head[x]=tot;
}
bool bfs()
{
	queue<int> q;
	memset(dis,-1,sizeof dis);
	q.push(1);dis[1]=0;
	while(!q.empty())
	{
		int x=q.front();q.pop();
		for(int i=head[x];i;i=nxt[i])
		{
			if(f[i]>0&&dis[to[i]]==-1)
			{
				dis[to[i]]=dis[x]+1;
				q.push(to[i]);
				if(to[i]==n+m+2) return true;
			}
		}
	}
	return false;
}
int dinic(int x,int flow)
{
	int a,temp=flow;
	if(x==n+m+2) return flow;
	for(int i=head[x];i;i=nxt[i])
	{
		if(f[i]>0&&dis[to[i]]==dis[x]+1)
		{
			a=dinic(to[i],min(f[i],temp));
			if(!a) dis[to[i]]=-1;
			temp-=a;
			f[i]-=a;
			f[i^1]+=a;
			if(!temp) break;
		}
	}
	return flow-temp;
}
int main()
{
	cin >> n >> m;
	int all=n+m+2;
	for(int a,i=1;i<=n;i++)
	{
		cin >> a;
		add(1+m+i,all,a);
		add(all,1+m+i,0);
	}
	int sum=0;
	for(int a,b,c,i=1;i<=m;i++)
	{
		cin >> a >> b >> c;
		sum+=c;
		add(1,1+i,c);add(1+i,1,0);
		add(1+i,1+m+a,inf);add(1+m+a,1+i,0);
		add(1+i,1+m+b,inf);add(1+m+b,1+i,0);
	}
	int ans=0;
	while(bfs())
	{
		// puts("Fuck");
		ans+=dinic(1,inf);
	}
	cout << sum - ans << endl;
	return 0;
}

     小结:最小割等于最大流

原文地址:https://www.cnblogs.com/ShuraK/p/8585016.html