BZOJ1497 NOI2006 最大获利 网络流

题意:给定N个用户和M个基站,每个基站有建设费用,每个用户由(a,b,c)描述,表示a b基站都建好后能获得c的收益,求最大获利

题解:

推荐论文:胡伯涛-《最小割模型在信息学竞赛中的应用》

然后说一下我的理解,我么按如下方式建图:每条边看作一个点,然后划分成二分图,左边一列是原图中的点也就是发送站,右边一列是原图中的边抽象成的点,然后建立原点s和汇点t,s向左边的所有点连容量为p[i]的边,然后右边的所有点向t连容量为c[j]的边,如果原图中一条边e连接点u v(e产生收益,前提是u v均已被选择),那么在二分图中将左边的u v分别和右边的e相连,容量为无穷大。

然后解释为何这样构图,根据最小割的性质,一条无穷大的边一定不会在最小割中,因此最小割中的边要么起点是s,要么终点是v

由于:净收益=(总收益-花费)

因此:最大净收益=全部收益-(最小收益+最小花费)

也就是说我们要让花费与没有被利用的边的边权和的和最小(也就是建造发送站的花费和不能产生收益的边的收益的和最小)。

实际上我们的决策过程就是在判定每个发送站是否需要建造。如果删掉s出发的指向u的边,就代表不建造u这个发送站;同时由于割的性质,必须要删除以u为基础的收益的那个点连向t的边才可以。因为建好的图中s出发的边代表花费,而连向t的边代表收益,因而这样跑出的最小割就是(最小总收益+最小花费)。说白了这就是个补集的思想,既然求不出最大的就先求最小的,然后用总的减去最小的不就是最大了的吧。

#include <queue>
#include <vector>
#include <cstdio>
#include <climits>
#include <cstring>
#include <cstdlib>
#include <iostream>
#include <algorithm>
using namespace std;

const int MAXN=50000+2;
const int MAXM=6000000+2;
struct EDGE{
    int u,c;
    EDGE(){}
    EDGE(int _u,int _c):u(_u),c(_c){}
}e[MAXM];
int N,M,cnt,d[MAXN],cur[MAXN],sum;
queue<int> q;
vector<int> tab[MAXN];

void Insert(int u,int v,int c){
    tab[u].push_back(cnt),e[cnt++]=EDGE(v,c);
    tab[v].push_back(cnt),e[cnt++]=EDGE(u,0);
}

bool BFS(int s,int t){
    memset(d,-1,sizeof(d));
    d[s]=0,q.push(s);

    int x;
    while(!q.empty()){
        x=q.front(),q.pop();
        for(int i=0;i<tab[x].size();i++)
            if(d[e[tab[x][i]].u]==-1 && e[tab[x][i]].c)
                d[e[tab[x][i]].u]=d[x]+1,q.push(e[tab[x][i]].u);
    }
    return d[t]>0;
}

int DFS(int x,int f,int t){
    if(x==t) return f;

    int flow,used=0;
    for(int i=cur[x];i<tab[x].size();i++)
        if(e[tab[x][i]].c && d[e[tab[x][i]].u]==d[x]+1){
            flow=DFS(e[tab[x][i]].u,min(f-used,e[tab[x][i]].c),t);
            e[tab[x][i]].c-=flow,e[tab[x][i]^1].c+=flow,used+=flow;
            if(e[tab[x][i]].c) cur[x]=i;
            if(f==used) return f;
        }

    if(!used) d[x]=-1;
    return used;
}

int Dinic(int s,int t){
    int ret=0;
    while(BFS(s,t)){
        memset(cur,0,sizeof(cur));
        ret+=DFS(s,INT_MAX,t);
    }
    return ret;
}

int main(){
    scanf("%d %d",&N,&M);
    for(int i=1,p;i<=N;i++){
        scanf("%d",&p);
        Insert(0,i,p);
    }
    for(int i=1,a,b,c;i<=M;i++){
        scanf("%d %d %d",&a,&b,&c);
        sum+=c;
        Insert(a,i+N,INT_MAX),Insert(b,i+N,INT_MAX),Insert(i+N,N+M+1,c);
    }
    printf("%d\n",sum-Dinic(0,N+M+1));

    return 0;
}
View Code

 

posted @ 2017-02-28 22:11  WDZRMPCBIT  阅读(130)  评论(0编辑  收藏  举报