您的位置:首页 > 其它

bzoj 1497 [NOI2006]最大获利

2017-01-25 10:49 225 查看
1497: [NOI2006]最大获利

Time Limit: 5 Sec Memory Limit: 64 MB

Submit: 4641 Solved: 2272

[Submit][Status][Discuss]

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

输入文件中第一行有两个正整数N和M 。第二行中有N个整数描述每一个通讯中转站的建立成本,依次为P1, P2, …, PN 。以下M行,第(i + 2)行的三个数Ai, Bi和Ci描述第i个用户群的信息。所有变量的含义可以参见题目描述。

Output

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

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

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。

【分析】

别人的题解里说这是最大权闭合子图模型,并给出了套路。(具体怎样我也不是很清楚)

以下讲解来自wmr

这是在有向图中的问题,且边依赖于点,有向图中存在点、边之间的依赖关系可以考虑最大权闭合子图

假设a与b之间有权值为c的边(根据题意是双向边)

那么我们可以建一个新节点,点的权值为c,并指向a点和b点(单向),同时断掉原本a,b之间的双向边,a,b的点的权值是它们的花费(负的)

那么对于原问题就转化成了求最大权闭合子图的问题了

————————————————————————————————————————————————————————————————

                           最大权闭合子图

定义:有向图中选出一个点集V’,必须满足对于V‘中的每个点,它的后继也在V’中,选出所有点权值和最大的点集V‘

算法:

弄一个源点S,汇点T,S连向所有权值为正的点,容量为点的权值,所有权值为负的点连向T,容量为点的权值的绝对值。

最后ans=图G中所有权值为正的节点的和-最小割

证明:http://wenku.baidu.com/link?url=Q7LKOvCRFeMQkY1WulrZTAHjN3ud8gbhuqUOKwPbwmGDAmCB0_URdEkJ59WKWVRGn9xSg9TgbWSmhhBIMxvGS2wMbENrxre6ZuSeO2v3mX7

然后这里我也来说一说:

1、首先因为原图中的边全是+inf,所以割边一定是S连出的边或者连向T的边

2、假设最小割割完后,S所在集合为A,T所在集合为B,那么A和B一定是闭合子图(S,T点除外)

因为如果假设A不是闭合子图,那么就说明A中的某一个节点的后继在B中,也就说明最小割把这条边给割掉了,但是原图中的边全是inf,所以不可能被割掉,所以假设不成立,所以A一定是闭合子图,B也一定是闭合子图(同理)

3、接下来我们只要说明对于最小割情况下的A集合,一定是最大的闭合权子图

对于随便任意一个割C(当然还是不会割掉容量inf的边)

则C为B中所有权值为正的点权和(即S连出的边被割掉的总量)+B中所有权值为负的点权绝对值和(即连向T的边被割掉的总量)(C=x1+y1)

设A集合对应的闭合图的权值和W,则W=A中权值为正的节点的权值和-A中权值为负的节点的权值绝对值和(W=x2-y2)

相加得W+C=x1+x2+y1-y2

y1和y2显然相等

所以W+C=x1+x2=原图中所有权值为正的点权和(这是个常数!)

所以C越小,W就越大,于是就相当于求最小割了

【代码】

//bzoj 1497
#include<iostream>
#include<cstring>
#include<cstdio>
#include<queue>
#define inf 1e9+7
#define fo(i,j,k) for(i=j;i<=k;i++)
using namespace std;
const int mxm=60005;
queue <int> q;
int n,m,s,t,cnt,ans,tans,tmp;
int head[mxm<<1],dis[mxm<<1];
struct node {int to,next,flow;} f[mxm<<4];
inline void add(int u,int v,int flow)
{
f[++cnt].to=v,f[cnt].next=head[u],f[cnt].flow=flow,head[u]=cnt;
f[++cnt].to=u,f[cnt].next=head[v],f[cnt].flow=0,head[v]=cnt;
}
inline bool bfs()
{
memset(dis,-1,sizeof dis);
q.push(s);
dis[s]=0;
while(!q.empty())
{
int u=q.front();
q.pop();
for(int i=head[u];i;i=f[i].next)
{
int v=f[i].to;
if(dis[v]<0 && f[i].flow>0)
{
dis[v]=dis[u]+1;
q.push(v);
}
}
}
if(dis[t]>0) return 1;
return 0;
}
inline int find(int u,int now)
{
int a,v,i,sum=0;
if(u==t) return now;
for(i=head[u];i;i=f[i].next)
{
v=f[i].to;
if(f[i].flow>0 && now>sum && dis[v]==dis[u]+1 && (a=find(v,min(now-sum,f[i].flow))))
{
f[i].flow-=a;
if(i&1) f[i+1].flow+=a;
else f[i-1].flow+=a;
sum+=a;
}
}
if (!sum) dis[u]=-1;
return sum;
}
int main()
{
int i,j,u,v,w;
scanf("%d%d",&n,&m);
t=n+m+1;
fo(i,1,n)
{
scanf("%d",&w);
add(i,t,w);
}
fo(i,1,m)
{
scanf("%d%d%d",&u,&v,&w);
add(n+i,u,inf),add(n+i,v,inf),add(s,n+i,w);
ans+=w;
}
while(bfs()) tmp+=find(s,inf);
printf("%d\n",ans-tmp);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: