【暖*墟】#网络流# 最大权闭合子图

【相关概念详解】

闭合图:有向图的一个点集,且这个点集的所有出边仍然指向该点集。

最大权闭合图:(每一个点有一个权值)在所有的合法闭合图中,点权之和最大的图。

处理问题:权值有正有负,重复选只算一次,选择有相互关联性 的问题。

首先有一个有向连通图(闭合图),每个点带有一个权值,例如:

造出一个超级源点S和一个超级汇点T,把S连边到所有带有正权的点上,每条边的容量是这个点的权

把所有带负权的点连边到T,每条边的容量是这个点的权的相反数(正值)。原来的边的容量设成无限大。

所有的点按权值的正负连接到s和t上,转换成一个边权值有向图。

最大权闭合图的最大权 = 所有正权值之和 — 最小割容量(最大流的值)

步骤归纳:1. 建立超级源点s,超级汇点t。

2. 所有点权为正数的点i,建边 s->i,容量为(正)点权。

3. 所有点权为负数的点i,建边i->t,容量为(负)点权绝对值。

4. 原图建图后,(互相关联影响的)边容量设为正无穷。

5.最大权闭合图的权值 = 正权点之和 - (s->t)最大流。

建图方法的推导

源点s可以理解为最大可获得的权值(即正权点之和)。

汇点t可以理解为最大的会损失的权值(负权点之和)。

我们现在要尽量的获得s,而避免t。

想选出最大权闭合图,则如果选定一个点,这个点的所有后继子孙点都必须选择。

因此,想拿正数,就不可避免的要取负数子孙后继点。

所以:要尽量选择正权点为起点,才有可能增大闭合图点集的权值和。

因此,我们从源点s向正权点连边(即只以正权点为起点)。

至于过程中遇到的负权点,我们让其流向t,即建立边 负权点->t的意图。

好,现在我们尽量去取正数点(直接从源点s起始),过程中遇到的负权点流向t。

现在就变成了:s->t的流量就是我们的损失。

即:我们希望流向t的流量flow尽量少,而从s流出的流量sum尽量多,

从s流出而没有流入t的流量(sum-flow)就是闭合图的最大权。

可能有种情况很懵逼:

若要选点,选2吧,权为-5;选1和2吧,权为-1;不如选空集,权为0。明显最后的选择是对的。

但按照上面的思路,从s流出4,所以损失最多为4,sum-flow=0。

所以对该图就产生这么一种结论:

选择1号点,和不选1号点,结果是相同的,我选的时候他会被损失完,效果等同于不选。

那不妨我一开始就把所有的正权点都给选了(满足从s流出的最多),让他们往后代流,

大不了被负权子孙点损失完,而那些没有被损失完的,就是我们统计下来的结果。

那个损失,就是s->t的最大流。于是得证:闭合图最大权 = 正权和sum - 最大流flow。

【例题1】【p4174】最大获利

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<map>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;

/*【p4174】最大获利
n个中转站,第i个的建立成本为pi。M个用户:ai与bi通讯,获利ci。
---> 求净获利最大。(净获利=获益之和–投入成本之和)*/

//【标签】网络流 + 最大权闭合子图

/*【分析】注意,‘用户’和‘中转站’是两种东西,不能混淆。*/

void reads(int &x){ //读入优化(正负整数)
    int fx=1;x=0;char ch_=getchar();
    while(ch_<‘0‘||ch_>‘9‘){if(ch_==‘-‘)fx=-1;ch_=getchar();}
    while(ch_>=‘0‘&&ch_<=‘9‘){x=x*10+ch_-‘0‘;ch_=getchar();}
    x*=fx; //正负号
}

const int N=1000019,inf=0x3f3f3f3f;

int n,m,s,t,tot=-1,head[N],dep[N],sum=0;

struct node{ int nextt,ver,w; }e[N];

void add(int x,int y,int z) //正向边权值为1,反向边权值为0
 { e[++tot].ver=y,e[tot].nextt=head[x],e[tot].w=z,head[x]=tot;
   e[++tot].ver=x,e[tot].nextt=head[y],e[tot].w=0,head[y]=tot; }

int bfs(){
    memset(dep,0,sizeof(dep)); //dep记录深度
    queue<int> q; while(!q.empty()) q.pop();
    dep[s]=1; q.push(s);
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=head[u];i!=-1;i=e[i].nextt)
            if((e[i].w>0)&&(dep[e[i].ver]==0)) //分层
                dep[e[i].ver]=dep[u]+1,q.push(e[i].ver);
    } if(dep[t]!=0) return 1;
      else return 0; //此时不存在分层图也不存在增广路
}

int dfs(int u,int lastt){ int ans=0;
    if(u==t) return lastt; //lastt:此点还剩余的流量
    for(int i=head[u];i!=-1&&ans<lastt;i=e[i].nextt)
        if((dep[e[i].ver]==dep[u]+1)&&(e[i].w!=0)){
            int f=dfs(e[i].ver,min(lastt-ans,e[i].w));
            if(f>0){ e[i].w-=f,e[i^1].w+=f,ans+=f; }
    } if(ans<lastt) dep[u]=-1; return ans;
}

int dinic(){ int ans=0; while(bfs()) ans+=dfs(s,1<<30); return ans; }

int main(){
    reads(n),reads(m); s=0,t=n+m+1; memset(head,-1,sizeof(head));
    for(int i=1,pi;i<=n;i++) reads(pi),add(i+m,t,pi); //编号点---T(相当于负权点)
    for(int i=1,ai,bi,ci;i<=m;i++){ reads(ai),reads(bi),reads(ci);
        sum+=ci; add(s,i,ci),add(i,ai+m,inf),add(i,bi+m,inf);
    } printf("%d\n",sum-dinic()); return 0; //↑↑把每个用户与两个中转站相连
}

【p4174】最大获利 // 网络流 + 最大权闭合子图【模板题】

【例题2】【p3749】寿司餐厅

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<map>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;

/*【p3749】寿司餐厅 */

//【标签】网络流 + 最大权闭合子图 + 读题/细节处理

/* 1. n<=2,种数只有 {不选}{1}{2}{1,2}{1;2} 10pts。
2. 60%数据m=0,即支出=∑每个代号吃了的种类数*代号。 */

/*【分析】感觉有点像网络流但我不会写0.0 最大权闭合子图。
日常%GXZ https://www.cnblogs.com/GXZlegend/p/6795784.html
对于每个点(i,j)(j>i),如果它被选择,那么点(i,j-1)和点(i+1,j)也一定被选择。
由此建点权图。对于点(i,j)(j>i),点权为d[i][j],并向点(i,j-1)和点(i+1,j)连边。
对于点(i,i),点权为d[i][i]-a[i](收益减去费用),并向编号a[i]连边。
对于编号p,点权为-m*p*p。所求为最大权闭合图,所以转化为网络流最小割来求。
最后的答案 : 闭合图最大权 = 正权和sum - 最大流flow。*/

void reads(int &x){ //读入优化(正负整数)
    int fx=1;x=0;char ch_=getchar();
    while(ch_<‘0‘||ch_>‘9‘){if(ch_==‘-‘)fx=-1;ch_=getchar();}
    while(ch_>=‘0‘&&ch_<=‘9‘){x=x*10+ch_-‘0‘;ch_=getchar();}
    x*=fx; //正负号
}

const int N=50019,inf=0x3f3f3f3f;

int a[N],d[110][110],id[110][110],cnt_;

int s,t,tot=-1,n1,n2,n3,head[N],dep[N]; //s为源点,t为汇点 

struct node{ int nextt,ver,w; }e[N];

void add(int x,int y,int z) //正向边权值为1,反向边权值为0
 { e[++tot].ver=y,e[tot].nextt=head[x],e[tot].w=z,head[x]=tot;
   e[++tot].ver=x,e[tot].nextt=head[y],e[tot].w=0,head[y]=tot; }

int bfs(){
    memset(dep,0,sizeof(dep)); //dep记录深度
    queue<int> q; while(!q.empty()) q.pop();
    dep[s]=1; q.push(s);
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=head[u];i!=-1;i=e[i].nextt)
            if((e[i].w>0)&&(dep[e[i].ver]==0)) //分层
                dep[e[i].ver]=dep[u]+1,q.push(e[i].ver);
    } if(dep[t]!=0) return 1;
      else return 0; //此时不存在分层图也不存在增广路
}

int dfs(int u,int lastt){ int ans=0;
    if(u==t) return lastt; //lastt:此点还剩余的流量
    for(int i=head[u];i!=-1&&ans<lastt;i=e[i].nextt)
        if((dep[e[i].ver]==dep[u]+1)&&(e[i].w!=0)){
            int f=dfs(e[i].ver,min(lastt-ans,e[i].w));
            if(f>0){ e[i].w-=f,e[i^1].w+=f,ans+=f; }
    } if(ans<lastt) dep[u]=-1; return ans;
}

int main(){

    int n,m,k=0,sum=0; reads(n),reads(m);

    memset(head,-1,sizeof(head)); //死也会忘记的...

    for(int i=1;i<=n;i++) reads(a[i]),k=max(k,a[i]);
    for(int i=1;i<=n;i++) for(int j=i;j<=n;j++)
        reads(d[i][j]),id[i][j]=++cnt_;

    s=0,t=cnt_+k+1; //cnt_个d(i,j),k种编号

    for(int i=1;i<=k;i++) add(cnt_+i,t,m*i*i);
    //↑↑k种编号向汇点连边(便于处理最后的m*x^2) 利用线性的‘互相影响’关系

    for(int i=1;i<=n;i++) add(id[i][i],cnt_+a[i],inf),
        d[i][i]-=a[i]; //点权-a[i](费用),向编号点a[i]连边

    for(int i=1;i<=n;i++) for(int j=i;j<=n;j++){ //最大全闭合子图的建边
        if(d[i][j]>0) add(s,id[i][j],d[i][j]),sum+=d[i][j]; //正权
        if(d[i][j]<0) add(id[i][j],t,-d[i][j]); //负权,向T建立全值为绝对值的边
        if(j>i) add(id[i][j],id[i][j-1],inf),add(id[i][j],id[i+1][j],inf);
    } //↑↑处理相关联有影响的性质:(i,j) -> (i,j-1)、(i+1,j)

    while(bfs()) sum-=dfs(s,inf); printf("%d\n",sum);

}

【p3749】寿司餐厅 // 网络流 + 最大权闭合子图 + 读题/细节处理

【例题3】【p2762】太空飞行问题

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<map>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;

//【p2762】太空飞行问题 // 网络流 + 最大权闭合子图 + 读入方式

/*【分析】S - m个实验 - n个仪器 - T */

const int N=1000019,inf=0x3f3f3f3f;

int n,m,s,t,tot=-1,head[N],dep[N],sum=0,flag=0;

int reads(int &x){ //读入优化(正负整数)
    int fx=1;x=0;char ch_=getchar();
    while(ch_<‘0‘||ch_>‘9‘){if(ch_==‘-‘)fx=-1;ch_=getchar();}
    while(ch_>=‘0‘&&ch_<=‘9‘){x=x*10+ch_-‘0‘;ch_=getchar();}
    x*=fx; return ch_==‘\r‘||ch_==‘\n‘?0:1; //十分厉害的读入...
}

struct node{ int nextt,ver,w; }e[N];

void add(int x,int y,int z) //正向边权值为1,反向边权值为0
 { e[++tot].ver=y,e[tot].nextt=head[x],e[tot].w=z,head[x]=tot;
   e[++tot].ver=x,e[tot].nextt=head[y],e[tot].w=0,head[y]=tot; }

int bfs(){
    memset(dep,0,sizeof(dep)); //dep记录深度
    queue<int> q; while(!q.empty()) q.pop();
    dep[s]=1; q.push(s);
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=head[u];i!=-1;i=e[i].nextt)
            if((e[i].w>0)&&(dep[e[i].ver]==0)) //分层
                dep[e[i].ver]=dep[u]+1,q.push(e[i].ver);
    } if(dep[t]!=0) return 1;
      else return 0; //此时不存在分层图也不存在增广路
}

int dfs(int u,int lastt){ int ans=0;
    if(u==t) return lastt; //lastt:此点还剩余的流量
    for(int i=head[u];i!=-1&&ans<lastt;i=e[i].nextt)
        if((dep[e[i].ver]==dep[u]+1)&&(e[i].w!=0)){
            int f=dfs(e[i].ver,min(lastt-ans,e[i].w));
            if(f>0){ e[i].w-=f,e[i^1].w+=f,ans+=f; }
    } if(ans<lastt) dep[u]=-1; return ans;
}

int dinic(){ int ans=0; while(bfs()) ans+=dfs(s,1<<30); return ans; }

int main(){

    reads(m),reads(n); s=0,t=n+m+1; memset(head,-1,sizeof(head));

    for(int i=1,pi,ai;i<=m;i++){ flag=reads(pi),sum+=pi,add(s,i,pi);
        while(flag) flag=reads(ai),add(i,ai+m,inf); }

    for(int i=1,ci;i<=n;i++) reads(ci),add(i+m,t,ci); //费用点---T

    int ans=sum-dinic(); //最大权闭合子图的最大权
    for(int i=1;i<=m;i++) if(dep[i]>0) cout<<i<<" "; printf("\n");
    for(int i=1;i<=n;i++) if(dep[i+m]>0) cout<<i<<" "; printf("\n");
    printf("%d\n",ans); //最大权闭合子图的最大权
}

【p2762】太空飞行问题 // 网络流 + 最大权闭合子图 + 读入方式 + 输出方案

【例题4】【poj2987】Fireing

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<map>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;

//【poj2987】Firing // 网络流 + 最大权闭合子图

/* 炒掉一个人能够获得b收益(b可以<0),但必须炒掉他的下属。求最大收益和此时最小裁员。 */

//【分析】把u是v的上司变成u—>v,运行最大权闭合子图

const ll N=1000019,inf=0x3f3f3f3f;

ll n,m,s,t,tot=-1,head[N],dep[N]; ll sum=0;

void reads(ll &x){ //读入优化(正负整数)
    ll fx=1;x=0;char ch_=getchar();
    while(ch_<‘0‘||ch_>‘9‘){if(ch_==‘-‘)fx=-1;ch_=getchar();}
    while(ch_>=‘0‘&&ch_<=‘9‘){x=x*10+ch_-‘0‘;ch_=getchar();}
    x*=fx; //正负号
}

struct node{ ll nextt,ver,w; }e[N];

void add(ll x,ll y,ll z) //正向边权值为1,反向边权值为0
 { e[++tot].ver=y,e[tot].nextt=head[x],e[tot].w=z,head[x]=tot;
   e[++tot].ver=x,e[tot].nextt=head[y],e[tot].w=0,head[y]=tot; }

ll bfs(){
    memset(dep,0,sizeof(dep)); //dep记录深度
    queue<ll> q; while(!q.empty()) q.pop();
    dep[s]=1; q.push(s);
    while(!q.empty()){
        ll u=q.front(); q.pop();
        for(ll i=head[u];i!=-1;i=e[i].nextt)
            if((e[i].w>0)&&(dep[e[i].ver]==0)) //分层
                dep[e[i].ver]=dep[u]+1,q.push(e[i].ver);
    } if(dep[t]!=0) return 1;
      else return 0; //此时不存在分层图也不存在增广路
}

ll dfs(ll u,ll lastt){ ll ans=0;
    if(u==t) return lastt; //lastt:此点还剩余的流量
    for(ll i=head[u];i!=-1&&ans<lastt;i=e[i].nextt)
        if((dep[e[i].ver]==dep[u]+1)&&(e[i].w!=0)){
            ll f=dfs(e[i].ver,min(lastt-ans,e[i].w));
            if(f>0){ e[i].w-=f,e[i^1].w+=f,ans+=f; }
    } if(ans<lastt) dep[u]=-1; return ans;
}

ll dinic(){ ll ans=0; while(bfs()) ans+=dfs(s,1<<30); return ans; }

bool vis[N];

ll get_(ll u){ ll ans=1; vis[u]=1; //要删去的所有人:e[i].w>0
    for(ll i=head[u];i!=-1;i=e[i].nextt)
     { ll v=e[i].ver; if(e[i].w>0&&!vis[v]) ans+=get_(v); } return ans; }

int main(){

    reads(n),reads(m); s=0,t=n+1; memset(head,-1,sizeof(head));

    for(ll i=1,bi;i<=n;i++){ reads(bi);
        if(bi>0) sum+=bi,add(s,i,bi); else add(i,t,-bi); }

    for(ll i=1,a,b;i<=m;i++) reads(a),reads(b),add(a,b,inf); //限制关系

    ll ans=sum-dinic(); //最大权闭合子图的最大权
    printf("%lld %lld\n",get_(s)-1,ans); //最大权闭合子图的最大权
}

【poj2987】Firing // 网络流 + 最大权闭合子图 + 不开ll见祖宗

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#include<queue>
#include<map>
#include<vector>
#include<cmath>
using namespace std;
typedef long long ll;

//【poj2987】Firing // 网络流 + 最大权闭合子图

/* 炒掉一个人能够获得b收益(b可以<0),但必须炒掉他的下属。求最大收益和此时最小裁员。 */

//【分析】把u是v的上司变成u—>v,运行最大权闭合子图

const ll N=1000019,inf=0x3f3f3f3f;

ll n,m,s,t,tot=-1,head[N],dep[N]; ll sum=0;

void reads(ll &x){ //读入优化(正负整数)
    ll fx=1;x=0;char ch_=getchar();
    while(ch_<‘0‘||ch_>‘9‘){if(ch_==‘-‘)fx=-1;ch_=getchar();}
    while(ch_>=‘0‘&&ch_<=‘9‘){x=x*10+ch_-‘0‘;ch_=getchar();}
    x*=fx; //正负号
}

struct node{ ll nextt,ver,w; }e[N];

void add(ll x,ll y,ll z) //正向边权值为1,反向边权值为0
 { e[++tot].ver=y,e[tot].nextt=head[x],e[tot].w=z,head[x]=tot;
   e[++tot].ver=x,e[tot].nextt=head[y],e[tot].w=0,head[y]=tot; }

ll bfs(){
    memset(dep,0,sizeof(dep)); //dep记录深度
    queue<ll> q; while(!q.empty()) q.pop();
    dep[s]=1; q.push(s);
    while(!q.empty()){
        ll u=q.front(); q.pop();
        for(ll i=head[u];i!=-1;i=e[i].nextt)
            if((e[i].w>0)&&(dep[e[i].ver]==0)) //分层
                dep[e[i].ver]=dep[u]+1,q.push(e[i].ver);
    } if(dep[t]!=0) return 1;
      else return 0; //此时不存在分层图也不存在增广路
}

ll dfs(ll u,ll lastt){ ll ans=0;
    if(u==t) return lastt; //lastt:此点还剩余的流量
    for(ll i=head[u];i!=-1&&ans<lastt;i=e[i].nextt)
        if((dep[e[i].ver]==dep[u]+1)&&(e[i].w!=0)){
            ll f=dfs(e[i].ver,min(lastt-ans,e[i].w));
            if(f>0){ e[i].w-=f,e[i^1].w+=f,ans+=f; }
    } if(ans<lastt) dep[u]=-1; return ans;
}

ll dinic(){ ll ans=0; while(bfs()) ans+=dfs(s,1<<30); return ans; }

int main(){

    reads(n),reads(m); s=0,t=n+1; memset(head,-1,sizeof(head));

    for(ll i=1,bi;i<=n;i++){ reads(bi);
        if(bi>0) sum+=bi,add(s,i,bi); else add(i,t,-bi); }

    for(ll i=1,a,b;i<=m;i++) reads(a),reads(b),add(a,b,inf); //限制关系

    ll ans=sum-dinic(),ans_=0; //最大权闭合子图的最大权
    for(ll i=1;i<=n;i++) if(dep[i]>0) ans_++;
    printf("%lld %lld\n",ans_,ans); //最大权闭合子图的最大权
}

【poj2987】Firing // 网络流 + 最大权闭合子图 + 第二种找路径的方法 time--

相关资料补充:https://www.cnblogs.com/dilthey/p/7565206.html

                                                       ——时间划过风的轨迹,那个少年,还在等你。

原文地址:https://www.cnblogs.com/FloraLOVERyuuji/p/10400914.html

时间: 2024-10-14 18:57:07

【暖*墟】#网络流# 最大权闭合子图的相关文章

BZOJ 4873 [Shoi2017]寿司餐厅 | 网络流 最大权闭合子图

链接 BZOJ 4873 题解 当年的省选题--还记得蒟蒻的我Day1 20分滚粗-- 这道题是个最大权闭合子图的套路题.严重怀疑出题人就是先画好了图然后照着图编了个3000字的题面.和我喜欢的妹子当年给别人写的情书一样长-- 最大权闭合子图 最大权闭合子图问题:一个有向图中,每个点带有一个权值(有正有负),有向边\(u \to v\)表示选\(u\)必须选\(v\),选出一些点使权值和最大,问权值和最大是多少. 最大权闭合子图的解法:网络流建图,对于每个点\(u\),设权值为\(w_u\),如

FZU2295 Human life:网络流-最大权闭合子图-二进制优化-第九届福建省大学生程序设计竞赛

目录 Catalog Solution: (有任何问题欢迎留言或私聊 && 欢迎交流讨论哦 Catalog Problem:Portal传送门 ?原题目描述在最下面. ?题意就是很裸的最大权闭合子图. ?推荐阅读:胡伯涛<最小割模型在信息学竞赛中的应用> ?完完全全的模板题:新疆大学五月月赛-D-勤奋的杨老师 ?本题题意:m(50)个任务,n个技能.完成每个任务由相应的收益,完成每个任务前必须学一些技能.有些技能由先修技能. ?有些任务不能同时完成. Solution: ?训练

hiho一下 第119周 #1398 : 网络流五&#183;最大权闭合子图 【最小割-最大流--Ford-Fulkerson 与 Dinic 算法】

#1398 : 网络流五·最大权闭合子图 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 周末,小Hi和小Ho所在的班级决定举行一些班级建设活动. 根据周内的调查结果,小Hi和小Ho一共列出了N项不同的活动(编号1..N),第i项活动能够产生a[i]的活跃值. 班级一共有M名学生(编号1..M),邀请编号为i的同学来参加班级建设活动需要消耗b[i]的活跃值. 每项活动都需要某些学生在场才能够进行,若其中有任意一个学生没有被邀请,这项活动就没有办法进行. 班级建设的活

[网络流24题] 太空飞行计划 (最大权闭合子图---网络最大流)

727. [网络流24题] 太空飞行计划 ★★☆ 输入文件:shuttle.in 输出文件:shuttle.out 简单对比 时间限制:1 s 内存限制:128 MB [问题描述] W 教授正在为国家航天中心计划一系列的太空飞行.每次太空飞行可进行一系列商业性实验而获取利润.现已确定了一个可供选择的实验集合E={E1,E2,…,Em},和进行这些实验需要使用的全部仪器的集合I={ I1, I2,…,In }.实验Ej 需要用到的仪器是I的子集Rj∈I.配置仪器Ik 的费用为ck 美元.实验Ej

网络流——最小割求最大权闭合子图

定义 有一个有向图,每一个点都有一个权值(可以为正或负或0),选择一个权值和最大的子图,使得每个点的后继都在子图里面,这个子图就叫最大权闭合子图. 如下图:  能选的子图有?,{4},{3,4},{2,4},{1,2,3,4},它们的权值分别为0,-1,5,-6,4. 所以最大权闭合子图为{3,4},权值为5. 解法 这个问题可以转化为最小割问题,用网络流解决. 从源点s向每个正权点连一条容量为权值的边,每个负权点向汇点t连一条容量为权值的绝对值的边,有向图原来的边容量全部为无限大.  求它的最

[HihoCoder1398]网络流五&#183;最大权闭合子图

题目大意:有$N$项活动$M$个人,每个活动$act_i$有一个正的权值$a_i$,每个人$stu_i$有一个负的权值$b_i$.每项活动能够被完成当且仅当该项活动所需的所有人到场.如何选择活动使最终权值总和最大?即对于给定的有向无环图,求出最大权闭合子图的权值. 结论:最大权闭合子图的权值等于所有正权点之和减去最小割. 思路:引理:1.最小割一定是简单割:2.简单割一定和一个闭合子图对应.即最小割一定对应一个闭合子图,且就是最大权闭合子图.证明(摘自HihoCoder):首先有割的容量C(S,

hdu-5855 Less Time, More profit(最大权闭合子图)

题目链接: Less Time, More profit Time Limit: 2000/1000 MS (Java/Others)     Memory Limit: 65536/65536 K (Java/Others) Problem Description The city planners plan to build N plants in the city which has M shops. Each shop needs products from some plants to

最大权闭合子图

所谓闭合子图就是给定一个有向图,从中选择一些点组成一个点集V.对于V中任意一个点,其后续节点都仍然在V中.比如: 在这个图中有8个闭合子图:?,{3},{4},{2,4},{3,4},{1,3,4},{2,3,4},{1,2,3,4} 小Ho:闭合子图我懂了,但是这跟我们这次的问题有啥关系呢? 小Hi:我们先把这次的问题转化为2分图.将N个活动看作A部,将M个学生看作B部.若第i个活动需要第j个学生,就连一条从A[i]到B[j]的有向边.比如对于例子: 假如选择A[1],则我们需要同时选择B[1

bzoj 1497 [NOI2006]最大获利【最大权闭合子图+最小割】

不要被5s时限和50000点数吓倒!大胆网络流!我一个5w级别的dinic只跑了1s+! 看起来没有最大权闭合子图的特征--限制,实际上还是有的. 我们需要把中转站看成负权点,把p看成点权,把客户看成正权点,把c看成点权,然后把中转站点a.b作为客户点的依赖点 s点向所有正权点连边,流量为点权:所有负权点向t连边,流量为负点权(即正数!) 对于所有有依赖关系的点,由客户点向中转站点连边,流量为inf,也就是最大权闭合子图中的向其依赖点连边 连边的意义详见:http://www.cnblogs.c