bzoj 1497 [NOI2006]最大获利

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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值