最小割:最大权闭合子图

最小割:最大权闭合子图

最大权闭合子图

给一个 DAG,你要选一个子图,如果一个点被选则要求它的所有后继节点都必须选,求可选的合法子图的最大权。

最小割模型

我们这样建模:

  • 对于边 \(u\to v\),连一条 \(u\to v\) 的容量为 \(\inf\) 的边。
  • 对于 \(val_x\ge 0\),连一条 \(S\to x\) 的容量为 \(val_x\) 的边。
  • 对于 \(val_x< 0\),连一条 \(x\to T\) 的容量为 \(-val_x\) 的边。

最后的答案是:所有正权和 减去 最小割

正确性可以这样考虑:

我们把割完后与 \(S\) 连通的点看做选入闭合子图,否则与 \(T\) 相连通即不选入。

那么对于一个正权点,发现其与 \(S\) 连通后,其后继的所有负权点都会与 \(T\) 相割。 这里满足了「闭合」。

那么那些与 \(S\) 相割的正权点就是不选入子图,在最小割中减去了,而与 \(T\) 相割的负权点就有负的贡献,也在最小割中减去了。这里说明了最终答案的「合法性」。

而根据贪心,一个选择的正权点的后继的正权点一定会被选,而一个负权点的前驱一定会选一个正权点。这里说明了最小割跑出的答案的「最优性」。

例题:P2805 [NOI2009] 植物大战僵尸

题意:选择一些点,点与点之间可能有依赖关系,要求前驱选完了其才可选,问选择的最大权。

解法

建图后我们先跑拓扑排序,把环上的点与环可达的点排除,因为这些点一定不可选。那么剩下的点之间一定是一张 DAG,这就是最大权闭合子图,我们按上述方法建图后跑网络流即可。

AC 代码:

const int D=1000,B=3e6;
const int inf=0x3f3f3f3f;
int to[B],nx[B],rl[B],st[D],cur[D],d[D],tot=1,S,T;
void _add(int x,int y,int z) {
	to[++tot]=y,nx[tot]=st[x],st[x]=tot,rl[tot]=z;
}
void add(int x,int y,int z) {
	_add(x,y,z),_add(y,x,0);
}
int go_on() {
	memset(d,0,sizeof d);
	d[S]=1;
	queue<int> q;
	q.push(S);
	while(Size(q)) {
		int u=q.front(); q.pop();
		for(int i=st[u];i;i=nx[i]) {
			int v=to[i];
			if(!d[v]&&rl[i]) d[v]=d[u]+1,q.push(v);
		}
	}
	return d[T]!=0;
}
int dfs(int x,int f) {
	if(x==T) return f;
	int res=f;
	for(int &i=cur[x];i;i=nx[i]) {
		int v=to[i];
		if(d[v]==d[x]+1&&rl[i]) {
			int t=dfs(v,min(res,rl[i]));
			rl[i]-=t,rl[i^1]+=t,res-=t;
			if(!res) return f;
		}
	}
	if(res==f) d[x]=-1;
	return f-res;
}
int dinic() {
	int res=0;
	while(go_on()) {
		memcpy(cur,st,sizeof cur);
		res+=dfs(S,inf);
	}
	return res;
}
const int N=D;
#define g(x,y) (x*m+y)
int n,m,sum=0;
int co[N],deg[N],bz[N];
vi t[N];
signed main(){
	read(n,m);
	S=n*m,T=S+1;
	fu(i,0,n*m) {
		read(co[i]);
		int w; read(w);
		while(w--) {
			int x,y; read(x,y);
			t[i].pb(g(x,y));
			add(g(x,y),i,inf);
			deg[g(x,y)]++;
		}
	}
	fu(i,0,n) fu(j,0,m-1) {
		t[g(i,j+1)].pb(g(i,j)),deg[g(i,j)]++;
		add(g(i,j),g(i,j+1),inf);
	}
	queue<int> q;
	fu(i,0,n*m) if(!deg[i]) q.push(i);
	while(Size(q)) {
		int u=q.front(); q.pop();
		bz[u]=1;
		for(int v:t[u]) {
			deg[v]--;
			if(!deg[v]) q.push(v);
		}
	}
	fu(i,0,n*m) if(bz[i]) {
		if(co[i]>=0) sum+=co[i],add(S,i,co[i]);
		else add(i,T,-co[i]);
	}
	write(sum-dinic());
	return 0;
}
posted @ 2025-03-04 22:07  dengchengyu  阅读(14)  评论(0)    收藏  举报