最大权闭合子图:一张图,一些点权值为正,一些点权值为负,选了一些点必须选择器后继节点,问权值最大的子图。
考虑最小割做法:
源点向点权为正的点连边,点权为负的点向汇点连边,其他点两两连边流量为inf,求最小割为答案。
设 点权为正的点与源点连边表示选择这个点
设 点权为负的点与源点连边表示不选择这个点
如果图联通:选择一个点又不选择后继节点,与闭合子图的定义矛盾,所以图必然不连通。
求一遍最小割:实际上是 不选择的点权为正的点+选择的点权为负的绝对值。保证最小。
那么:最大权闭合子图的权值为:正点权和-(不选择的点权为正的点+选择的点权为负的绝对值)
Firing
POJ - 2987#include<cstdio> #include<cmath> #include<queue> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N=1e5+500; typedef long long ll; const ll INF=1e17; int n,m,S,T; struct Dinic{ int head[N],dep[N]; int ecnt,cnt; int vis[N]; struct edge{ int v,next;ll flow; }e[N*10]; void init(){ memset(head, -1, sizeof head); ecnt = cnt=0; memset(vis,0,sizeof vis); } void addedge(int u, int v, ll flow) { e[ecnt].v=v;e[ecnt].flow=flow;e[ecnt].next=head[u];head[u]=ecnt++; e[ecnt].v=u;e[ecnt].flow=0;e[ecnt].next=head[v];head[v]=ecnt++; } bool BFS(){//分层图找增广路 memset(dep,0,sizeof dep); queue<int>Q; Q.push(S);dep[S]=1; while(!Q.empty()){ int u=Q.front();Q.pop(); for(int i=head[u];~i;i=e[i].next){ if(e[i].flow&&!dep[e[i].v]){ dep[e[i].v]=dep[u]+1; Q.push(e[i].v); } } } return dep[T]; } void cut(int u){ vis[u]=1; cnt++; for(int i=head[u];~i;i=e[i].next){ int v=e[i].v; if(e[i].flow&&!vis[v])cut(v); } } ll DFS(int u,ll f){//推进新流 if(u==T||f==0)return f; ll w,used=0; for(int i=head[u];~i;i=e[i].next){ if(e[i].flow&&dep[e[i].v]==dep[u]+1){ w=DFS(e[i].v,min(f,e[i].flow));//多路增广 e[i].flow-=w;e[i^1].flow+=w; used+=w;f-=w; } } if(!used)dep[u]=-1;//炸点优化 return used; } ll maxflow() { ll ans=0; while(BFS()){ ans+=DFS(S,INF); } return ans; } }dinic; int main(){ scanf("%d %d",&n,&m); dinic.init(); S=0;T=n+50; ll sum=0; for(int i=1;i<=n;i++){ ll p;scanf("%lld",&p); if(p>0)dinic.addedge(S,i,p),sum+=p; else dinic.addedge(i,T,-p); } while(m--){ int u,v;scanf("%d %d",&u,&v); dinic.addedge(u,v,INF); } ll ans=sum-dinic.maxflow(); dinic.cut(S); cout<<dinic.cnt-1<<" "<<ans<<endl; // system("pause"); return 0; }View Code
求最大权闭合子图,输出选择方案,
构建最大流求闭合子图权,那么所有流量为正的点即为被选择的点。