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

最大获利

时间:2017-05-22 15:05:13      阅读:240      评论:0      收藏:0      [点我收藏+]

标签:print   max   ble   size   数据   状态   矛盾   style   content   

最大获利

时间限制: 1 Sec  内存限制: 128 MB

题目描述

新的技术正冲击着手机通讯市场,对于各大运营商来说,这既是机遇,更是挑战。  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  公司可以有选择的建立一些中转站(投入成本),为一些用户提供服务并获得收益(获益之和)。那么如何选择最终建立的中转站才能让公司的净获利最大呢?(净获利  =  获益之和  –  投入成本之和)

输入

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

第二行中有  N  个整数描述每一个通讯中转站的建立成本,依次为P1,  P2,  …,  PN  。

以下  M  行,第(i  +  2)行的三个数  Ai,  Bi  和  Ci  描述第  i  个用户群的信息。

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

输出

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

样例输入

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

样例输出

4

提示

 

原题要求:


只需要向输出文件输出一行,行内不得有多余空白字符,行末须有一个换行/回车符,格式不对不能得分。



提示:最大权闭合子图


小Ho:这次的问题好像还是很麻烦的样子啊。

小Hi:没错,小Ho你有什么想法么?

小Ho:我么?我能想到只有枚举啦。因为每一项活动都只有举行和不举行两种状态,因此我直接用O(2^N)的枚举,再对选出来的情况进行计算。最后选出最大的方案。

小Hi:这很明显会超过时间限制吧。

小Ho:我知道啊,那有什么好的方法么?

小Hi:当然有啊,这次我们需要解决的是闭合子图问题。

小Ho:这个闭合子图是啥?

小Hi:所谓闭合子图就是给定一个有向图,从中选择一些点组成一个点集V。对于V中任意一个点,其后续节点都仍然在V中。比如:

技术分享

在这个图中有8个闭合子图:?,{3},{4},{2,4},{3,4},{1,3,4},{2,3,4},{1,2,3,4}

小Ho:闭合子图我懂了,但是这跟我们这次的问题有啥关系呢?

小Hi:我们先把这次的问题转化为2分图。将N个活动看作A部,将M个学生看作B部。若第i个活动需要第j个学生,就连一条从A[i]到B[j]的有向边。比如对于例子:

技术分享

假如选择A[1],则我们需要同时选择B[1],B[2]。那么选择什么活动和其需要的学生,是不是就刚好对应了这个图中的一个闭合子图呢?

小Ho:你这么一说好像还真是。如果把活跃值算作权值,A部的节点包含有正的权值,B部的节点是负的权值。那么我们要求的也就是一个权值最大的闭合子图了?

小Hi:没错,我们要求解的正是最大权闭合子图。它的求解方法是使用网络流,因此我们需要将这个图再进一步转化为网络流图。

对于一般的图来说:首先建立源点s和汇点t,将源点s与所有权值为正的点相连,容量为权值;将所有权值为负的点与汇点t相连,容量为权值的绝对值;权值为0的点不做处理;同时将原来的边容量设置为无穷大。举个例子:

技术分享

对于我们题目中的例子来说,其转化的网络流图为:

技术分享

上图中黑边表示容量无穷大的边。

小Ho:转化模型这一步看上去不是太难,然后呢?

小Hi:先说说结论吧,最大权闭合子图的权值等于所有正权点之和减去最小割。

接下来来证明这个结论,首先我们要证明两个引理:

1. 最小割一定是简单割

简单割指得是:割(S,T)中每一条割边都与s或者t关联,这样的割叫做简单割。

因为在图中将所有与s相连的点放入割集就可以得到一个割,且这个割不为正无穷。而最小割一定小于等于这个割,所以最小割一定不包含无穷大的边。因此最小割一定一个简单割。

2. 简单割一定和一个闭合子图对应

闭合子图V和源点s构成S集,其余点和汇点t构成T集。

首先证明闭合子图是简单割:若闭合子图对应的割(S,T)不是简单割,则存在一条边(u,v),u∈S,v∈T,且c(u,v)=∞。说明u的后续节点v不在S中,产生矛盾。

接着证明简单割是闭合子图:对于V中任意一个点u,u∈S。u的任意一条出边c(u,v)=∞,不会在简单割的割边集中,因此v不属于T,v∈S。所以V的所有点均在S中,因此S-s是闭合子图。



由上面两个引理可以知道,最小割也对应了一个闭合子图,接下来证明最小割就是最大权的闭合子图。

首先有割的容量C(S,T)=T中所有正权点的权值之和+S中所有负权点的权值绝对值之和。

闭合子图的权值W=S中所有正权点的权值之和-S中所有负权点的权值绝对值之和。

则有C(S,T)+W=T中所有正权点的权值之和+S中所有正权点的权值之和=所有正权点的权值之和。

所以W=所有正权点的权值之和-C(S,T)

由于所有正权点的权值之和是一个定值,那么割的容量越小,W也就越大。因此当C(S,T)取最小割时,W也就达到了最大权。

小Ho:我懂了,因为最小割也对应了一个闭合子图,因此它是可以被取得的,W也才能够到达最大权值。

小Hi:没错,这就是前面两条引理的作用。

小Ho:那么最小割的求解就还是用最大流来完成好了!

小Hi:嗯,那就交给你了。
 
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<cstdlib>
#include<queue>
#include<stack>
#include<ctime>
#include<vector>
using namespace std;
int n,m;
struct node
{
    int next,to,cap;
}edge[500001];
int head[200001],size=1;
void putin(int from,int to,int cap)
{
    size++;
    edge[size].next=head[from];
    edge[size].to=to;
    edge[size].cap=cap;
    head[from]=size;
}
void in(int from,int to,int cap)
{
    putin(from,to,cap);
    putin(to,from,0);
}
int dist[200001],numbs[200001];
void bfs(int src,int des)
{
    int i;
    queue<int>mem;
    mem.push(des);
    dist[des]=0;numbs[0]++;
    while(!mem.empty())
    {
        int x=mem.front();mem.pop();
        for(i=head[x];i!=-1;i=edge[i].next)
        {
            int y=edge[i].to;
            if(edge[i].cap==0&&dist[y]==0&&y!=des)
            {
                dist[y]=dist[x]+1;
                numbs[dist[y]]++;
                mem.push(y);
            }
        }
    }
    return;
}
int dfs(int src,int flow,int des)
{
    if(src==des)return flow;
    int i,low=0,mindist=n+m+2;
    for(i=head[src];i!=-1;i=edge[i].next)
    {
        int y=edge[i].to;
        if(edge[i].cap)
        {
            if(dist[y]==dist[src]-1)
            {
                int t=dfs(y,min(flow-low,edge[i].cap),des);
                edge[i].cap-=t;
                edge[i^1].cap+=t;
                low+=t;
                if(dist[src]>=n+m+2)return low;
                if(low==flow)break;
            }
            mindist=min(mindist,dist[y]+1);
        }
    }
    if(!low)
    {
        if(!(--numbs[dist[src]]))dist[0]=n+m+2;
        ++numbs[dist[src]=mindist];
    }
    return low;
}
int ISAP(int src,int des)
{
    int ans=0;
    bfs(src,des);
    while(dist[0]<n+m+2)ans+=dfs(src,2e8,des);
    return ans;
}
int cnt;
int main()
{
    //freopen("4.in","r",stdin);
    int i,j;
    memset(head,-1,sizeof(head));
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&j);
        in(m+i,m+n+1,j);
    }
    for(i=1;i<=m;i++)
    {
        int a,b,dis;
        scanf("%d%d%d",&a,&b,&dis);
        in(i,m+a,2e8);
        in(i,m+b,2e8);
        in(0,i,dis);
        cnt+=dis;
    }
    int maxflow=ISAP(0,n+m+1);
    printf("%d",cnt-maxflow);
}

 

最大获利

标签:print   max   ble   size   数据   状态   矛盾   style   content   

原文地址:http://www.cnblogs.com/huangdalaofighting/p/6888943.html

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