NOI2006 最大获利(最大权闭合子图)

codevs 1789 最大获利

2006年NOI全国竞赛

 时间限制: 2 s
 空间限制: 128000 KB
 
题目描述 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。 

最大权闭合子图

源点向所有用户连流量为收益的边

所有中转站向汇点连流量为成本的边

用户所需要的中转站,由用户向需要的中转站连inf边

然后用所有用户收益和-最小割

#include<cstdio>
#include<queue>
#include<algorithm>
#define N 55010
#define M 50001
#define inf 2e9
using namespace std;
int n,m,tot=1;
int src,dec,sum;
int lev[N],cur[N],front[N],cap[M*10],to[M*10],next[M*10];
queue<int>q;
void add(int u,int v,int w)
{
    to[++tot]=v;next[tot]=front[u];front[u]=tot;cap[tot]=w;
    to[++tot]=u;next[tot]=front[v];front[v]=tot;cap[tot]=0;
}
bool bfs()
{
    for(int i=0;i<=dec;i++) {lev[i]=-1;cur[i]=front[i];}
    while(!q.empty()) q.pop();
    q.push(src);lev[src]=0;
    while(!q.empty())
    {
        int now=q.front();q.pop();
        for(int i=front[now];i;i=next[i])
        {
            int t=to[i];
            if(cap[i]>0&&lev[t]==-1)
            {
                lev[t]=lev[now]+1;
                if(t==dec) return true;
                q.push(t);
            }
        }
    }
    return false;
}
int dinic(int now,int flow)
{
    if(now==dec) return flow;
    int rest=0,delta;
    for(int & i=cur[now];i;i=next[i])
    {
        int t=to[i];
        if(cap[i]>0&&lev[t]>lev[now])
        {
            delta=dinic(t,min(cap[i],flow-rest));
            if(delta)
            {
                cap[i]-=delta;cap[i^1]+=delta;
                rest+=delta;if(rest==flow) break;
            }
        }
    
    }
    if(rest!=flow) lev[now]=-1;
    return rest;
}
int main()
{
    scanf("%d%d",&n,&m);
    dec=n+m+1;
    int x,y,z;
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&x);
        add(i,dec,x);
    }
    for(int i=1;i<=m;i++)
    {
        scanf("%d%d%d",&x,&y,&z);
        add(src,n+i,z);
        add(n+i,x,inf);
        add(n+i,y,inf);
        sum+=z;
    }
    while(bfs()) 
    sum-=dinic(src,inf);
    printf("%d",sum);
}

空间问题,今天下午第n次开小空间

原文地址:https://www.cnblogs.com/TheRoadToTheGold/p/6511732.html