HDU 3879 Base Station(最小割的最大权闭包 最大获利)经典

来源:互联网 发布:淘宝详情图片大小 编辑:程序博客网 时间:2024/05/20 00:11

Base Station

Time Limit: 5000/2000 MS (Java/Others)    Memory Limit: 65768/32768 K (Java/Others)
Total Submission(s): 2040    Accepted Submission(s): 863


Problem Description
A famous mobile communication company is planning to build a new set of base stations. According to the previous investigation, n places are chosen as the possible new locations to build those new stations. However, the condition of each position varies much, so the costs to built a station at different places are different. The cost to build a new station at the ith place is Pi (1<=i<=n).

When complete building, two places which both have stations can communicate with each other.

Besides, according to the marketing department, the company has received m requirements. The ith requirement is represented by three integers Ai, Bi and Ci, which means if place Aiand Bi can communicate with each other, the company will get Ci profit.

Now, the company wants to maximize the profits, so maybe just part of the possible locations will be chosen to build new stations. The boss wants to know the maximum profits.
 

Input
Multiple test cases (no more than 20), for each test case:
The first line has two integers n (0<n<=5000) and m (0<m<=50000).
The second line has n integers, P1 through Pn, describes the cost of each location.
Next m line, each line contains three integers, Ai, Bi and Ci, describes the ith requirement.
 

Output
One integer each case, the maximum profit of the company.
 

Sample Input
5 51 2 3 4 51 2 32 3 41 3 31 4 24 5 3
 

Sample Output
4
 

Author
liulibo
 

Source
2011 Multi-University Training Contest 5 - Host by BNU 

题意:

公司得到了一共N个可以作为通讯信号中转站的地址,而由于这些地址的地理位置差异,在不同的地方建造通讯中转站需要投入的成本也是不一样的,所幸在前期调查之后这些都是已知数据:建立第i个通讯中转站需要的成本为Pi(1≤i≤N)。
•另外公司调查得出了所有期望中的用户群,一共M个。关于第i个用户群的信息概括为Ai, Bi和Ci:这些用户会使用中转站Ai和中转站Bi进行通讯,公司可以获益Ci。(1≤i≤M, 1≤Ai, Bi≤N)
•THU集团的CS&T公司可以有选择的建立一些中转站(投入成本),为一些用户提供服务并获得收益(获益之和)。那么如何选择最终建立的中转站才能让公司的净获利最大呢?(净获利 = 获益之和 - 投入成本之和)
解题:网上一解析最大权闭包链接
下面是我个人的理解:
对于这样一个有向图,把每个边编一个号,再把每条边也看作是一个点,这个图的点就可以分成两个部分点X部分的点为信号站点,Y部分的点为边新加入的点,建图时:X部分的点与源点相连,边权为在当前点建立信号站点的花费,Y部分的所有点与汇点相连,边权为这个边的收益,X点与Y点建边则为X点引一条边到 原图与哪条边相连,边权为INF,所有Y里的每个点都有两条边与X中的点相连。ans = 所有的总收益 -最小割(建信号站的总成本+未选择的用户应得到的收益)。 假设第k条边为< u , v , c > ,1:如果最小割  割掉了u 和 v 那么绝对不会割掉第k条边与汇点相连的边的收益,2:如果只割掉 u 与 v 其中一个点或两个点都没有被割断 ,那么必然会割掉第K条边与汇点相连的边的收益。与条件相符合,所以可以用最小割来做。
#include<stdio.h>#include<string.h>#include<queue>#include<algorithm>using namespace std;#define captype __int64const int MAXN = 100010;   //点的总数const int MAXM = 400010;    //边的总数const int INF = 1<<30;struct EDG{    int to,next;    captype cap,flow;} edg[MAXM];int eid,head[MAXN];int gap[MAXN];  //每种距离(或可认为是高度)点的个数int dis[MAXN];  //每个点到终点eNode 的最短距离int cur[MAXN];  //cur[u] 表示从u点出发可流经 cur[u] 号边int pre[MAXN];void init(){    eid=0;    memset(head,-1,sizeof(head));}//有向边 三个参数,无向边4个参数void addEdg(int u,int v,captype c,captype rc=0){    edg[eid].to=v; edg[eid].next=head[u];    edg[eid].cap=c; edg[eid].flow=0; head[u]=eid++;    edg[eid].to=u; edg[eid].next=head[v];    edg[eid].cap=rc; edg[eid].flow=0; head[v]=eid++;}captype maxFlow_sap(int sNode,int eNode, int n){//n是包括源点和汇点的总点个数,这个一定要注意    memset(gap,0,sizeof(gap));    memset(dis,0,sizeof(dis));    memcpy(cur,head,sizeof(head));    pre[sNode] = -1;    gap[0]=n;    captype ans=0;  //最大流    int u=sNode;    while(dis[sNode]<n){   //判断从sNode点有没有流向下一个相邻的点        if(u==eNode){   //找到一条可增流的路            captype Min=INF ;            int inser;            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to])    //从这条可增流的路找到最多可增的流量Min            if(Min>edg[i].cap-edg[i].flow){                Min=edg[i].cap-edg[i].flow;                inser=i;            }            for(int i=pre[u]; i!=-1; i=pre[edg[i^1].to]){                edg[i].flow+=Min;                edg[i^1].flow-=Min;  //可回流的边的流量            }            ans+=Min;            u=edg[inser^1].to;            continue;        }        bool flag = false;  //判断能否从u点出发可往相邻点流        int v;        for(int i=cur[u]; i!=-1; i=edg[i].next){            v=edg[i].to;            if(edg[i].cap-edg[i].flow>0 && dis[u]==dis[v]+1){                flag=true;                cur[u]=pre[v]=i;                break;            }        }        if(flag){            u=v;            continue;        }        //如果上面没有找到一个可流的相邻点,则改变出发点u的距离(也可认为是高度)为相邻可流点的最小距离+1        int Mind= n;        for(int i=head[u]; i!=-1; i=edg[i].next)        if(edg[i].cap-edg[i].flow>0 && Mind>dis[edg[i].to]){            Mind=dis[edg[i].to];            cur[u]=i;        }        gap[dis[u]]--;        if(gap[dis[u]]==0) return ans;  //当dis[u]这种距离的点没有了,也就不可能从源点出发找到一条增广流路径                                        //因为汇点到当前点的距离只有一种,那么从源点到汇点必然经过当前点,然而当前点又没能找到可流向的点,那么必然断流        dis[u]=Mind+1;//如果找到一个可流的相邻点,则距离为相邻点距离+1,如果找不到,则为n+1        gap[dis[u]]++;        if(u!=sNode) u=edg[pre[u]^1].to;  //退一条边    }    return ans;}int main(){    int n,m,vs,vt,u,v;    captype c;    while(scanf("%d%d",&n,&m)>0)    {        init();        vs=0; vt=n+m+1;        for(int i=1; i<=n; i++)        {            scanf("%I64d",&c);            addEdg(vs , i , c);        }        captype ans = 0;        while(m){            scanf("%d%d%I64d",&u,&v,&c);            addEdg(u , m+n , INF);            addEdg(v , m+n , INF);            addEdg(m+n , vt , c);            m--;            ans+=c;        }        ans -= maxFlow_sap( vs , vt , vt+1);        printf("%I64d\n",ans);    }}


0 0