码迷,mamicode.com
首页 > 其他好文 > 详细

NOI2006 最大获利(网络流)

时间:2015-08-04 21:10:28      阅读:89      评论:0      收藏:0      [点我收藏+]

标签:网络流

题目描述:

新的技术正冲击着手机通讯市场,对于各大运营商来说,这既是机遇,更是挑战。THU 集团旗下的 CS&T 通讯公司在新一代通讯技术血战的前夜,需要做太多的准备工作,仅就站址选择一项,就需要完成前期市场研究、站址勘测、最优化等项目。 在前期市场调查和站址勘测之后, 公司得到了一共 N个可以作为通讯信号中转站的地址,而由于这些地址的地理位置差异,在不同的地方建造通讯中转站需要投入的成本也是不一样的,所幸在前期调查之后这些都是已知数据:建立第 i个通讯中转站需要的成本为Pi(1≤i≤N) 。 另外公司调查得出了所有期望中的用户群,一共 M 个。关于第i个用户群的信息概括为Ai, Bi 和Ci:这些用户会使用中转站 Ai和中转站 Bi进行通讯,公司可以获益Ci。 (1≤i≤M, 1≤Ai, Bi≤N) THU 集团的CS&T公司可以有选择的建立一些中转站(投入成本) ,为一些用户提供服务并获得收益(获益之和) 。那么如何选择最终建立的中转站才能让公司的净获利最大呢?(净获利 = 获益之和 – 投入成本之和) 


思路:经典的网络流模型,建立模型的时候可以参考二分图模型的建立。把中转站视作x部,用户群视作y部,用户群需要的中转站之间连一条有向边(用户群向中转站),容量为无穷大。虚拟一个源点,向x部每个点连一条有向边,容量为中转站的成本。再虚拟一个汇点,y部每个点向汇点连一条有向边,权值为用户群的获利。求得该图的最小割。用用户群获利的和减去最小割就是要求的最大获利,为什么呢?我们求得的最小割是没有用到的用户群的获利加上用到的中转站的成本(画个图理解理解吧),然后总利润减去最小割就是最大获利了。

#include<cstdio>
#include<vector>
#define MAXN 60005
#define INF 999999999
#define BIG 123456789
#define Min(a,b) a<b?a:b
using namespace std;
struct T
{
	int v;
	int w;
	int op;//保存反向边编号
	T(){}
	T(int a,int b,int c)
	{
		v = a;
		w = b;
		op = c;
	}
};
vector<T> g[MAXN];//残留网络
int d[MAXN];//距离标号
int vd[MAXN];//标号为i的节点个数
int m,n,flow,sum,N;//N:顶点数,flow:最大流(本题实则是最小割),sum:所有利润和

int aug(int i,int augco)//顶点i,augco:从i为起点的最大增广容量 
{
	int j,augc = augco,mind = N-1,delta;
	if(i == N) return augco;//到达汇点
		
	for(j = 0; j < g[i].size(); j++)//枚举i的邻接点
	{
		int v = g[i][j].v;
		int w = g[i][j].w;
		if(w)//如果有边到j,最先忘写这句了,结果疯狂TLE
		{
			if(d[i] == d[v]+1)//(i,j)为可进入弧
			{
				delta = Min(augc,w);//求出经(i,j)的可增广最大值
				delta = aug(v,delta);//递归增广,返回沿(i,j)的实际增广量
				g[i][j].w -= delta;//更新残留网络
				g[v][g[i][j].op].w += delta;
				augc -= delta; //augc记录剩下的需要增广的量
				if(d[1] >= N) return augco - augc;//结束,向上一层返回经过i的实际增广量
				if(augc == 0) break; //已经到达可增广上界,提前跳出
			}
			mind = Min(mind,d[v]);//更新最小的邻接点标号
		}
	}
	if(augco == augc)//如果从i点无法增广
	{
		vd[d[i]]--;//标号为d[i]的结点数-1
		if(vd[d[i]] == 0) d[1] = N;//GAP优化
		d[i] = mind+1; //更新标号
		vd[d[i]]++;//新标号的结点数+1
	}
	return augco - augc;//向上一层返回经过i的实际增广量
}
void sap()
{
	vd[0] = N;
	while(d[1] < N-1)
		flow += aug(1,BIG);
}

int main()
{
	int a,b,c;
	scanf("%d%d",&n,&m);
	for(int i = 1; i <= n; i++)
	{
		scanf("%d",&a);
		g[1].push_back(T(i+1,a,g[i+1].size()));
		g[i+1].push_back(T(1,0,g[i].size()-1));
	}
	for(int i = n+2; i <= n+1+m; i++)
	{
		scanf("%d%d%d",&a,&b,&c);
		g[a+1].push_back(T(i,INF,g[i].size()));
		g[i].push_back(T(a+1,0,g[a+1].size()-1));
		g[b+1].push_back(T(i,INF,g[i].size()));
		g[i].push_back(T(b+1,0,g[b+1].size()-1));
		g[i].push_back(T(m+n+2,c,g[m+n+2].size()));
		g[m+n+2].push_back(T(i,0,g[i].size()-1));
		sum += c;
	}
	N = m+n+2;
	sap();
	printf("%d\n",sum - flow);
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

NOI2006 最大获利(网络流)

标签:网络流

原文地址:http://blog.csdn.net/cqbzwja/article/details/47281197

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!