• [洛谷P4147] NOI2006 最大获利


    问题描述

    新的技术正冲击着手机通讯市场,对于各大运营商来说,这既是机遇,更是挑战。THU 集团旗下的 CS&T 通讯公司在新一代通讯技术血战的前夜,需要做太多的准备工作,仅就站址选择一项,就需要完成前期市场研究、站址勘测、最优化等项目。

    在前期市场调查和站址勘测之后,公司得到了一共 N 个可以作为通讯信号中转站的地址,而由于这些地址的地理位置差异,在不同的地方建造通讯中转站需要投入的成本也是不一样的,所幸在前期调查之后这些都是已知数据:建立第 i个通讯中转站需要的成本为 (P_i) (1≤i≤N)。

    另外公司调查得出了所有期望中的用户群,一共 M 个。关于第 i 个用户群的信息概括为 (A_i) , (B_i)(C_i) :这些用户会使用中转站 A i 和中转站 B i 进行通讯,公司可以获益 (C_i) 。(1≤i≤M, 1≤(A_i) , (B_i) ≤N)

    THU 集团的 CS&T 公司可以有选择的建立一些中转站(投入成本),为一些用户提供服务并获得收益(获益之和)。那么如何选择最终建立的中转站才能让公司的净获利最大呢?(净获利 = 获益之和 – 投入成本之和)

    输入格式

    输入文件中第一行有两个正整数 N 和 M 。

    第二行中有 N 个整数描述每一个通讯中转站的建立成本,依次为 (P_1 , P_2 , …,P_N)

    以下 M 行,第(i + 2)行的三个数 (A_i , B_i)(C_i) 描述第 i 个用户群的信息。

    所有变量的含义可以参见题目描述。

    输出格式

    你的程序只要向输出文件输出一个整数,表示公司可以得到的最大净获利。

    样例输入

    5 5
    1 2 3 4 5
    1 2 3
    2 3 4
    1 3 3
    1 4 2
    4 5 3

    样例输出

    4

    说明

    样例:选择建立 1、2、3 号中转站,则需要投入成本 6,获利为 10,因此得到最大收益 4。

    100%的数据中:N≤5 000,M≤50 000,0≤(C_i)≤100,0≤(P_i) ≤100。

    解析

    经典的二元最小割模型。

    既然求最大收益,我们可以转化为理论总收益(所有可能收益的和)减去成本。考虑问题的的实际意义。从源点向每一个基站连边,容量为建造的成本,删去这条边表示放弃建立这个基站。对于每个用户群向汇点连边,容量为用户群的收益,删去这条边表示放弃这个用户群的收益。每个基站向包含自己的用户群连边,容量为inf,表示不能删。然后求最小割即可。

    代码

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <queue>
    #define N 5002
    #define M 50002
    using namespace std;
    const int inf=1<<30;
    int head[N+M*4],ver[M*8],nxt[M*8],cap[M*8],l;
    int n,m,i,s,t,dis[N+M*4],ans;
    int read()
    {
    	char c=getchar();
    	int w=0;
    	while(c<'0'||c>'9') c=getchar();
    	while(c<='9'&&c>='0'){
    		w=w*10+c-'0';
    		c=getchar();
    	}
    	return w;
    }
    void insert(int x,int y,int z)
    {
    	ver[l]=y;
    	cap[l]=z;
    	nxt[l]=head[x];
    	head[x]=l;
    	l++;
    	ver[l]=x;
    	nxt[l]=head[y];
    	head[y]=l;
    	l++;
    }
    bool bfs()
    {
    	queue<int> q;
    	memset(dis,-1,sizeof(dis));
    	q.push(s);
    	dis[s]=0;
    	while(!q.empty()){
    		int x=q.front();
    		q.pop();
    		for(int i=head[x];i!=-1;i=nxt[i]){
    			int y=ver[i];
    			if(dis[y]==-1&&cap[i]>0){
    				dis[y]=dis[x]+1;
    				q.push(y);
    			}
    		}
    	}
    	return (dis[t]>0);
    }
    int dfs(int x,int flow)
    {
    	if(x==t||flow==0) return flow;
    	int ans=0;
    	for(int i=head[x];i!=-1;i=nxt[i]){
    		int y=ver[i];
    		if(dis[y]==dis[x]+1&&cap[i]>0){
    			int a=dfs(y,min(flow,cap[i]));
    			flow-=a;
    			ans+=a;
    			cap[i]-=a;
    			cap[i^1]+=a;
    		}
    		if(flow==0) break;
    	}
    	if(flow) dis[x]=-1;
    	return ans;
    }
    int main()
    {
    	memset(head,-1,sizeof(head));
    	n=read();m=read();
    	t=n+m+1;
    	for(i=1;i<=n;i++){
    		int w=read();
    		insert(s,i,w);
    	}
    	for(i=1;i<=m;i++){
    		int a=read(),b=read(),c=read();
    		insert(a,n+i,inf);
    		insert(b,n+i,inf);
    		insert(n+i,t,c);
    		ans+=c;
    	}
    	while(bfs()) ans-=dfs(s,inf);
    	printf("%d
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    现实世界的Windows Azure:采访圣地亚哥公共安全小组的技术经理Adrian Gonzalez
    Casablanca发布:一个用C++访问云的本地类库
    现实世界的Windows Azure:采访IDV解决方案的副总经理Scott Caulk
    Rock Paper Azure Challenge春季比赛来了!
    上海职场六大关键词完全搜集之:才市
    理解 Delphi 的类(十一) 深入类中的方法[8] 抽象方法与抽象类
    理解 Delphi 的类(十一) 深入类中的方法[10] 构造方法与析构方法
    TScreen 类 获取字体列表
    理解 Delphi 的类(十一) 深入类中的方法[9] 不能被覆盖的方法与不能被继承的类
    理解 Delphi 的类(十一) 深入类中的方法[13] 方法的调用约定
  • 原文地址:https://www.cnblogs.com/LSlzf/p/12208156.html
Copyright © 2020-2023  润新知