BZOJ_P1497&Codevs_P1789 最大获利(最大权闭合图最小割最大流)

84 篇文章 0 订阅
48 篇文章 0 订阅

最大获利 2006年NOI全国竞赛
时间限制: 2 s
空间限制: 128000 KB
题目等级 : 大师 Master
题目描述 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。

仔细观察问题可以发现,对于获得某一用户群的价值必须建立关于用户群的两个基站,容易想到最大权闭合路
对于每个基站权值为负,而对于每个用户群权值为正,将超级源点与用户群相连,流量为价值,将超级基站与超级汇点相连,流量同样为价值,做超级源点到超级汇点的最大流即可,对于关于最大权闭合图中最优性证明可以去网上参考有关资料;

#include<cstdio>
#include<climits>
#include<cstring>
#include<vector>
#include<queue>
#include<iostream>
using namespace std;
#define N 5005
#define M 50002
#define INF INT_MAX/3*2
struct NetWork{
    struct Edge{
        int fr,to,cap,flow;
    };
    vector<Edge> edge;vector<int> g[M+N];
    bool b[N+M];int d[N+M],p[N+M],cur[N+M];
    int n,m,flow,S,T,ans;

    void in(int &x){
        x=0;char ch=getchar();
        while(ch>'9'||ch<'0') ch=getchar();
        while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
        return;
    }

    void Add_Edge(int fr,int to,int cap,int flow){
        edge.push_back(Edge{fr,to,cap,flow});
        edge.push_back(Edge{to,fr,0,0});
        int t=edge.size();
        g[fr].push_back(t-2);g[to].push_back(t-1);
    }

    void init(){
        in(n),in(m);S=0,T=n+m+1;int x,y,z;
        for(int i=1;i<=n;i++){
            in(x);Add_Edge(i,T,x,0);
        }
        for(int i=1;i<=m;i++){
            in(x);in(y);in(z);
            Add_Edge(S,n+i,z,0);Add_Edge(n+i,x,INF,0);Add_Edge(n+i,y,INF,0);
            ans+=z;
        }
    }

    bool bfs(){
        memset(b,0,sizeof(b));memset(d,-1,sizeof(d));
        queue<int> q;int x;
        d[S]=0;b[S]=true;q.push(S);
        while(!q.empty()){
            x=q.front();q.pop();
            for(int i=0;i<g[x].size();i++){
                Edge &e=edge[g[x][i]];
                if(!b[e.to]&&e.cap>e.flow){
                    b[e.to]=true;d[e.to]=d[x]+1;q.push(e.to);
                }
            }
        }
        return b[T];
    }

    void MaxFlow(){
        int k,x,mine,minf;
        while(bfs()){
            memset(cur,0,sizeof(cur));k=0;x=S;
            for(;;){
                if(x==T){
                    mine=-1;minf=INF;
                    for(int i=0;i<k;i++){
                        Edge &e=edge[p[i]];
                        if(e.cap-e.flow<minf){
                            minf=e.cap-e.flow;mine=i;
                        }
                    }
                    for(int i=0;i<k;i++){
                        edge[p[i]].flow+=minf;
                        edge[p[i]^1].flow-=minf;
                    }
                    flow+=minf;x=edge[p[mine]].fr;k=mine;
                }
                for(int &i=cur[x];i<g[x].size();i++){
                    Edge &e=edge[g[x][i]];
                    if(e.cap>e.flow&&d[x]+1==d[e.to]) break;
                }
                if(cur[x]<g[x].size()){
                    p[k++]=g[x][cur[x]];x=edge[g[x][cur[x]]].to;
                }
                else{
                    if(!k) break;
                    d[x]=-1;k--;x=edge[p[k]].fr;
                }
            }
        }
    }

    void solve(){
        init();MaxFlow();printf("%d",ans-flow);
    }
}s;
int main(){
    s.solve();return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值