最大权闭合图小结

最大权闭合图

昨天在做多校联赛的时候偶然碰上的,学习了下这个,首先推荐胡伯涛的《最小割模型在信息学竞赛中的应用》。

定义

一个有向图 的闭合图(closure) G=(V,E) 是该有向图的一个点集,且该点集的所有出边都还指向该点集。即闭合图内的任意点的任意后继也一定在闭合图中。更形式化地说,闭合图是这样的一个点集 VV ,满足对于 uV 引出的 u,vE 。那么给每个点 v 分配一个点权Wv(任意实数,可正可负)。大权闭合图(maximum weight closure),是一个点权之和大的闭合图,即大化 vVWv

那么,我们的做法就是当 Wv 是负的时候,建一条边 v,t 容量为 Wv ,当 Wv 是正的时候,建一条边 s,v 容量为 Wv ,然后对于原来的边 u,v 保持不变,建一条 u,v 容量为 inf 的边。那么易证这个图的在最小割的情况下,和 s 相联通的子图是最大权闭合子图。
所以我们做法就是跑一次最大流,然后最大权闭合图的权值就是总收益-最大流。

POJ2987

这是一个非常裸地最大权闭合图,当 Wv 是负的时候,建一条边 v,t 容量为 Wv ,当 Wv 是正的时候,建一条边 s,v 容量为 Wv ,然后对于原来的边 u,v 保持不变,建一条 u,v 容量为 inf 的边。
然后直接按照原理跑一下最大流就好。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <queue>
#include <map>
#include <cmath>
#include <vector>
using namespace std;
#define N 5010
#define pi acos(-1.0)
#define inf 0x3f3f3f3f
#define pb(x) push_back((x))
typedef long long ll;
typedef unsigned long long ull;
struct edge{
    int to,c,rev;
};
vector<edge> g[N];
ll sum;
int level[N],iter[N];
int n,m;
void addnode(int a,int b,int c){
    g[a].push_back((edge){b,c,g[b].size()});
    g[b].push_back((edge){a,0,g[a].size()-1});

}
void build_graph(int s,int t,int n,int m){
    int tmp,u,v;
    for(int i=1;i<=n;i++){
        scanf("%d",&tmp);
        if(tmp>=0){
            addnode(s,i,tmp);
            sum+=tmp;
        }
        else addnode(i,t,-tmp);
    }
    for(int i=1;i<=m;i++){
        scanf("%d%d",&u,&v);
        addnode(u,v,inf);
    }

}

bool bfs(int s,int t){
    memset(level,-1,sizeof(iter));
    queue <int> q;
    level[s]=0;
    q.push(s);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=0;i<g[u].size();i++){
            edge e=g[u][i];
            if(e.c>0&&level[e.to]==-1){
                level[e.to]=level[u]+1;
                q.push(e.to);
            }
        }
    }
    if(level[t]==-1)return false;
    return true;
}

int dfs(int u,int t,int f){
    if(u==t) return f;
    for(int &i=iter[u];i<g[u].size();i++){
        edge &e=g[u][i];
        if(e.c>0&&level[e.to]>level[u]){
            int d=dfs(e.to,t,min(e.c,f));
            if(d>0){
                e.c-=d;
                g[e.to][e.rev].c+=d;
                return d;
            }
        }
    }
    return 0;
}


int main(){
    int n,m;
    while(~scanf("%d%d",&n,&m)){
        sum=0;
        build_graph(0,n+1,n,m);
        ll ans=0;
        while(bfs(0,n+1)){
            memset(iter,0,sizeof(iter));
            int f;
            while((f=dfs(0,n+1,inf))>0){
                ans+=f;
            }
        }
        bfs(0,n+1);
        int fire=0;
        for(int i=1;i<=n;i++) if(level[i]>0) fire++;
        printf("%d %lld\n",fire,sum-ans);
        for(int i=0;i<=n+1;i++) g[i].clear();
    }
    return 0;
}

HDU4971

水题。

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <queue>
#include <map>
#include <cmath>
#include <vector>
using namespace std;
#define N 110
#define pi acos(-1.0)
#define inf 0x3f3f3f3f
#define pb(x) push_back((x))
typedef long long ll;
typedef unsigned long long ull;

struct edge{
    int to,c,rev;
};
vector<edge> g[N];
ll sum;
int level[N],iter[N];
int n,m;
void addnode(int a,int b,int c){
    g[a].push_back((edge){b,c,g[b].size()});
    g[b].push_back((edge){a,0,g[a].size()});

}
void build_graph(int s,int t,int n,int m){
    int tmp,u,v,k;
    for(int i=1;i<=n;i++){
        scanf("%d",&tmp);
        addnode(s,i,tmp);
        sum+=tmp;
    }
    for(int i=1;i<=m;i++){
        scanf("%d",&tmp);
        addnode(i+n,t,tmp);
    }
    for(int i=1;i<=n;i++){
        scanf("%d",&k);
        for(int j=1;j<=k;j++){
            scanf("%d",&tmp);
            addnode(i,n+tmp+1,inf);
        }
    }
    for(int i=1;i<=m;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&tmp);
            if(tmp) addnode(n+i,n+j,inf);
        }
    }
}

bool bfs(int s,int t){
    memset(level,-1,sizeof(iter));
    queue <int> q;
    level[s]=0;
    q.push(s);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=0;i<g[u].size();i++){
            edge e=g[u][i];
            if(e.c>0&&level[e.to]==-1){
                level[e.to]=level[u]+1;
                q.push(e.to);
            }
        }
    }
    if(level[t]==-1)return false;
    return true;
}

int dfs(int u,int t,int f){
    if(u==t) return f;
    for(int &i=iter[u];i<g[u].size();i++){
        edge &e=g[u][i];
        if(e.c>0&&level[e.to]>level[u]){
            int d=dfs(e.to,t,min(e.c,f));
            if(d>0){
                e.c-=d;
                g[e.to][e.rev].c+=d;
                return d;
            }
        }
    }
    return 0;
}


int main(){
    int n,m,t,cnt=0;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d",&n,&m);
        sum=0;
        build_graph(0,n+m+1,n,m);
        ll ans=0;
        while(bfs(0,n+m+1)){
            memset(iter,0,sizeof(iter));
            int f;
            while((f=dfs(0,n+1+m,inf))>0){
                ans+=f;
            }
        }
        printf("Case #%d: %d\n",++cnt,sum-ans);
        for(int i=0;i<=n+m+1;i++) g[i].clear();
    }
    return 0;
}

HDU5855

首先膜拜一下朝鲜队的出题大哥,题目质量太好了。
这道题我们直接二分最短的时间,然后对于每一次时间判断一下最大权闭合图是不是大于L就好

#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <set>
#include <queue>
#include <map>
#include <cmath>
#include <vector>
using namespace std;
#define N 410
#define pi acos(-1.0)
#define inf 0x3f3f3f3f
#define pb(x) push_back((x))
typedef long long ll;
typedef unsigned long long ull;
struct edge{
    int to,c,rev;
};
vector<int> shop[N];
vector<edge> g[N];
int level[N];
int iter[N];
int n,m,L,tmp1,tmp2;
int pt[N],pay[N],pro[N];
int sum;
inline void addnode(int a,int b,int c){
    g[a].push_back((edge){b,c,g[b].size()});
    g[b].push_back((edge){a,0,g[a].size()-1});
}

inline void build_graph(int mid){//建图
    for(int i=1;i<=m;i++){
        bool f=true;
        addnode(0,i,pro[i]);
        for(int j=0;j<shop[i].size();j++){
            addnode(i,m+shop[i][j],inf);
        }
    }
    for(int i=1;i<=n;i++){
        if(pt[i]>mid) addnode(m+i,n+m+1,inf);
        else addnode(m+i,n+m+1,pay[i]);
    }
}


inline bool bfs(int s,int t){
    memset(level,-1,sizeof(level));
    queue <int> q;
    level[s]=0;
    q.push(s);
    while(!q.empty()){
        int u=q.front();
        q.pop();
        for(int i=0;i<g[u].size();i++){
            edge &e=g[u][i];
            if(e.c>0&&level[e.to]<0){
                level[e.to]=level[u]+1;
                q.push(e.to);
            }
        }
    }
    if(level[t]==-1) return false;
    return true;
}

inline int dfs(int u,int t,int f){
    if(u==t) return f;
    for(int &i=iter[u];i<g[u].size();i++){
        edge &e=g[u][i];
        if(e.c>0&&level[e.to]>level[u]){
            int d=dfs(e.to,t,min(f,e.c));
            if(d>0){
                e.c-=d;
                g[e.to][e.rev].c+=d;
                return d;
            }
        }
    }
    return 0;
}

inline int dinic(int mid){
    int ans=0;
    build_graph(mid);
    while(bfs(0,n+m+1)){
        memset(iter,0,sizeof(iter));
        int f;
        while((f=dfs(0,n+m+1,inf))>0){
            ans+=f;
        }//循环增广
    }
    for(int i=0;i<=n+m+1;i++) g[i].clear();
    if(ans==0) return 0;
    //printf("ans=%d %d\n",ans,sum-ans);
    return sum-ans;
}
int main(){
    int t;
    int maxpt;
    int cnt=0;
    scanf("%d",&t);
    while(t--){
        scanf("%d%d%d",&n,&m,&L);
        int maxpt=0;
        sum=0;
        for(int i=1;i<=n;i++){
            scanf("%d%d",&pay[i],&pt[i]);
            maxpt=max(pt[i],maxpt);
        }
        for(int i=1;i<=m;i++){
            scanf("%d",&pro[i]);
            sum+=pro[i];
            scanf("%d",&tmp1);
            for(int j=1;j<=tmp1;j++){
                scanf("%d",&tmp2);
                shop[i].push_back(tmp2);
            }
        }
        int r=maxpt+1,l=0;
        tmp1=0;
        while(l<r){
            //printf("%d %d ",l,r);
            int mid=(l+r)/2;
            if(l+1==r){
                tmp1=dinic(r);
                //printf("tmp1= %d\n",tmp1);
                if(tmp1>=L){
                    int ptmp1=dinic(l);
                    //printf("tmp1= %d\n",ptmp1);
                    if(ptmp1>=L) tmp1=ptmp1;
                    else l=r;
                }
                else l=r+1;
                break;
            }
            tmp1=dinic(mid);
            if(tmp1>=L) r=mid;
            else l=mid;
            //printf("end= %d\n",tmp1);
        }
        printf("Case #%d: ",++cnt);
        if(l<=maxpt){
            printf("%d %d\n",l,tmp1);
        }
        else printf("impossible\n");
        for(int i=1;i<=m;i++) shop[i].clear();
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值