最大获利 2006年NOI全国竞赛
时间限制: 2 s
空间限制: 128000 KB
题目等级 : 大师 Master
题目描述 Description
新的技术正冲击着手机通讯市场,对于各大运营商来说,这既是机遇,更是 挑战。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 公司可以有选择的建立一些中转站(投入成本),为一些 用户提供服务并获得收益(获益之和)。那么如何选择最终建立的中转站才能让 公司的净获利最大呢?(净获利 = 获益之和 – 投入成本之和)
输入描述 Input Description
输入文件中第一行有两个正整数 N 和 M 。 第二行中有 N 个整数描述每一个通讯中转站的建立成本,依次为 P1, P2, …, PN 。 以下 M 行,第(i + 2)行的三个数 Ai, Bi和 Ci描述第 i 个用户群的信息。 所有变量的含义可以参见题目描述。
输出描述 Output Description
你的程序只要向输出文件输出一个整数,表示公司可以得到的最大净获利。
样例输入 Sample Input
5 5
1 2 3 4 5
1 2 3
2 3 4
1 3 3
1 4 2
4 5 3
样例输出 Sample Output
4
数据范围及提示 Data Size & Hint
选择建立 1、2、3 号中转站,则需要投入成本 6,获利为 10,因此得到最大 收益 4。
80%的数据中:N≤200,M≤1 000。
100%的数据中:N≤5 000,M≤50 000,0≤Ci≤100,0≤Pi≤100。
仔细观察问题可以发现,对于获得某一用户群的价值必须建立关于用户群的两个基站,容易想到最大权闭合路
对于每个基站权值为负,而对于每个用户群权值为正,将超级源点与用户群相连,流量为价值,将超级基站与超级汇点相连,流量同样为价值,做超级源点到超级汇点的最大流即可,对于关于最大权闭合图中最优性证明可以去网上参考有关资料;
#include<cstdio>
#include<climits>
#include<cstring>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define N 5005
#define M 50002
#define INF INT_MAX/3*2
struct NetWork{
struct Edge{
int fr,to,cap,flow;
};
vector<Edge> edge;vector<int> g[M+N];
bool b[N+M];int d[N+M],p[N+M],cur[N+M];
int n,m,flow,S,T,ans;
void in(int &x){
x=0;char ch=getchar();
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return;
}
void Add_Edge(int fr,int to,int cap,int flow){
edge.push_back(Edge{fr,to,cap,flow});
edge.push_back(Edge{to,fr,0,0});
int t=edge.size();
g[fr].push_back(t-2);g[to].push_back(t-1);
}
void init(){
in(n),in(m);S=0,T=n+m+1;int x,y,z;
for(int i=1;i<=n;i++){
in(x);Add_Edge(i,T,x,0);
}
for(int i=1;i<=m;i++){
in(x);in(y);in(z);
Add_Edge(S,n+i,z,0);Add_Edge(n+i,x,INF,0);Add_Edge(n+i,y,INF,0);
ans+=z;
}
}
bool bfs(){
memset(b,0,sizeof(b));memset(d,-1,sizeof(d));
queue<int> q;int x;
d[S]=0;b[S]=true;q.push(S);
while(!q.empty()){
x=q.front();q.pop();
for(int i=0;i<g[x].size();i++){
Edge &e=edge[g[x][i]];
if(!b[e.to]&&e.cap>e.flow){
b[e.to]=true;d[e.to]=d[x]+1;q.push(e.to);
}
}
}
return b[T];
}
void MaxFlow(){
int k,x,mine,minf;
while(bfs()){
memset(cur,0,sizeof(cur));k=0;x=S;
for(;;){
if(x==T){
mine=-1;minf=INF;
for(int i=0;i<k;i++){
Edge &e=edge[p[i]];
if(e.cap-e.flow<minf){
minf=e.cap-e.flow;mine=i;
}
}
for(int i=0;i<k;i++){
edge[p[i]].flow+=minf;
edge[p[i]^1].flow-=minf;
}
flow+=minf;x=edge[p[mine]].fr;k=mine;
}
for(int &i=cur[x];i<g[x].size();i++){
Edge &e=edge[g[x][i]];
if(e.cap>e.flow&&d[x]+1==d[e.to]) break;
}
if(cur[x]<g[x].size()){
p[k++]=g[x][cur[x]];x=edge[g[x][cur[x]]].to;
}
else{
if(!k) break;
d[x]=-1;k--;x=edge[p[k]].fr;
}
}
}
}
void solve(){
init();MaxFlow();printf("%d",ans-flow);
}
}s;
int main(){
s.solve();return 0;
}