bzoj 1497(最大权闭合图/最小割)

 

传送门

题意:

有$n$个通信塔,建立第$i$个通讯塔需要花费$p_i$元。同时有$m$个人,对于第$i$个人,如果$a_i$号塔以及$b_i$号塔被建了,则会赚$c_i$元。问最大的收益。

分析:

最大权闭合图的经典的模型。

所谓闭合图,即要满足一定的依赖关系,即一个事件要发生,它需要的所有前提也都一定要发生。

而最大权闭合图即为点权最大的闭合图。

而这类问题的一个比较经典的建图方法是:

  1. 将能够使收益增加的点与超级源点相连,边的容量为将会增加的收益值(设之为$a_i$
  2. 将能够使收益减少的点与超级汇点相连,边的容量为将会减少的收益值(设之为$b_i$
  3. 将原来的闭合图之间连接一条容量为无穷的边。

最后的闭合图的最大权为$\sum a_i-res$。而$res$的值为需要损失的收益且其值一定为原图的最小割。


简单说明(全是胡扯) :

要使得$\sum a_i-res$最大,则显然我们需要损失的收益值$res$最小。而因为在闭合图中,某些事件(亦或者说,某些能使得收益增加的事件)的发生,必然会导致它后面所依赖的所有事件(可能部分事件会使得收益减少)均发生,而在这个过程中,可能会产生很大的损失。

因此我们考虑将部分边割掉。

我们发现,如果我们将某个能使得收益增加的点与源点的边割掉,则必然会导致我们得不到该点带来的贡献,这个我们可以看作损失;同理,如果我们将某个能使得收益减少的点与汇点的边割掉,我们则需要付出一定代价,这也可以看错损失。

而我们目前需要求得的是最小的损失$res$,因此我们只需要求出原图的最小割即可。


而对于上面的题目,我们发现,使得每个人满意是能够使得答案增加的,而使得每个人满意的制约条件是会使得答案减少的,因此我们把人和超级源点相连,灯塔与超级汇点相连,最后跑一边最大流,最后的答案即为$\sum c_i-maxflow$

代码:

#include <bits/stdc++.h>
using namespace std;
const int maxn=1000005;
const int inf=0x3f3f3f3f;
struct Node{
    int to,val,next;
}q[maxn<<1];
int head[maxn],cnt=0,dep[maxn],cur[maxn],vis[maxn];
int sp,ep,maxflow;
void init(){
    memset(head,-1,sizeof(head));
    cnt=2,maxflow=0;
}
void add_edge(int from,int to,int val){
    q[cnt].to=to;
    q[cnt].val=val;
    q[cnt].next=head[from];
    head[from]=cnt++;

    q[cnt].to=from;
    q[cnt].val=0;
    q[cnt].next=head[to];
    head[to]=cnt++;
}
bool bfs(int n){
    for(int i=0;i<=n;i++){
        cur[i]=head[i],dep[i]=0x3f3f3f3f;
        vis[i]=0;
    }
    dep[sp]=0;
    queue<int>que;
    que.push(sp);
    while(!que.empty()){
        int x=que.front();
        que.pop();
        vis[x]=0;
        for(int i=head[x];i!=-1;i=q[i].next){
            int to=q[i].to;
            if(dep[to]>dep[x]+1&&q[i].val){
                dep[to]=dep[x]+1;
                if(!vis[to]){
                    que.push(to);
                    vis[to]=1;
                }
            }
        }
    }
    if(dep[ep]!=inf) return true;
    else return false;
}
int dfs(int x,int flow){
    int rlow=0;
    if(x==ep){
        maxflow+=flow;
        return flow;
    }
    int used=0;
    for(int i=cur[x];i!=-1;i=q[i].next){
        cur[x]=i;
        int to=q[i].to;
        if(q[i].val&&dep[to]==dep[x]+1){
            if(rlow=dfs(to,min(flow-used,q[i].val))){
                used+=rlow;
                q[i].val-=rlow;
                q[i^1].val+=rlow;
                if(used==flow) break;
            }
        }
    }
    return used;
}
int dinic(int n){
    while(bfs(n)){
        dfs(sp,inf);
    }
    return maxflow;
}
int main()
{
    int n,m;
    scanf("%d%d",&n,&m);
    init();
    sp=0,ep=n+m+1;
    for(int i=1;i<=n;i++){
        int vals;
        scanf("%d",&vals);
        add_edge(i+m,ep,vals);
    }
    int res=0;
    for(int i=1;i<=m;i++){
        int from,to,val;
        scanf("%d%d%d",&from,&to,&val);
        add_edge(sp,i,val);
        add_edge(i,m+from,inf);
        add_edge(i,m+to,inf);
        res+=val;
    }
    printf("%d\n",res-dinic(n+m+1));
    return 0;
}
posted @ 2019-05-26 01:15  ChenJr  阅读(245)  评论(0编辑  收藏  举报