网络流最小割[模板:最大权闭合图]

一张具有流量的图上,源点是s,汇点是t

割的定义:去掉一些边,使得s->t不存在任何流量

最小割:去掉代价最小的一些边,使得s->t不存在任何流量

定理:从s跑到t的最大流就是s到t的最小割,最大流=最小割

感性的理解: 最 大 流 包 含 所 有 s 流 向 t 的 可 能 最大流包含所有s流向t的可能 st

所以割至少要包含这些流向t的边,就可以使s不可达t

有 一 类 问 题 叫 做 最 大 权 闭 合 图 \color{Red}有一类问题叫做最大权闭合图

你暂时不需要知道这是什么,先看下面这道例题

P2762 太空飞行计划问题(最大全闭合图模板)

对 于 这 道 题 , 先 说 做 法 , 再 来 证 明 . 对于这道题,先说做法,再来证明. ,,.

构 图 \color{Red}构图

Ⅰ . 源 点 s 向 实 验 连 一 条 流 量 为 实 验 利 润 的 边 Ⅰ.源点s向实验连一条流量为实验利润的边 .s

Ⅱ . 每 个 实 验 向 对 应 的 器 材 连 一 条 容 量 无 穷 的 边 Ⅱ.每个实验向对应的器材连一条容量无穷的边 .

Ⅲ . 器 材 向 汇 点 t 连 一 条 流 量 为 器 材 代 价 的 边 Ⅲ.器材向汇点t连一条流量为器材代价的边 .t

结论

s s s t t t跑最大流以求出 s s s t t t的最小割

用所有实验的理论和-最小割就是答案.

证明

回忆一下题目

题目意思是选择了某个实验,就必须选择对应的一些器材,求最大收益

那 我 们 求 最 小 割 时 就 是 让 s 不 能 到 t 那我们求最小割时就是让s不能到t st

假 如 最 小 割 选 择 割 掉 s 到 实 验 u 的 边 , 那 么 最 小 割 的 代 价 加 上 u 实 验 利 润 \color{Red}假如最小割选择割掉s到实验u的边,那么最小割的代价加上u实验利润 su,u

最后用利润总和(包含u的利润)减去最小割(包含u的利润),相当于没选这个实验

假 如 最 小 割 没 有 割 掉 s 到 实 验 u 的 边 , 那 么 设 u 对 应 的 器 材 是 v \color{Red}假如最小割没有割掉s到实验u的边,那么设u对应的器材是v su,uv

v 到 t 的 边 一 定 会 被 割 掉 , 假 如 没 有 被 割 掉 , s 到 t 就 联 通 了 , 就 不 是 一 个 割 v到t的边一定会被割掉,假如没有被割掉,s到t就联通了,就不是一个割 vt,,st,

割 掉 v 到 t 的 边 , 最 小 割 的 代 价 加 上 v 器 材 的 花 费 割掉v到t的边,最小割的代价加上v器材的花费 vt,v

最 后 用 利 润 总 和 ( 包 含 u 的 利 润 ) 减 去 最 小 割 ( 包 含 u 对 应 器 材 花 费 ) , 相 当 于 选 u 实 验 且 选 u 实 验 需 要 的 器 材 最后用利润总和(包含u的利润)减去最小割(包含u对应器材花费),相当于选u实验且选u实验需要的器材 (u)(u),uu

综上所诉,求最小割就考虑到了全部的情况

#include <bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
const int inf=1e9;
int read() {
	static char c;
	static int x;
	if(c == '\n') return c = 0, -1;
	while(!isdigit(c = getchar())); 
	x = c ^ 48;
	while(isdigit(c = getchar()))
		x = x * 10 + (c ^ 48);
	return x;
}
int s,t,n,m,maxflow,sumn;
struct p{
	int to,flow,nxt;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int flow){
	d[++cnt]=(p){v,flow,head[u] },head[u]=cnt;
}
int dis[maxn];
bool bfs()
{
	queue<int>q;
	for(int i=0;i<=2*t;i++)	dis[i]=0;
	dis[s]=1;
	q.push(s);
	while( !q.empty() )
	{
		int u=q.front(); q.pop();
		for(int i=head[u];i;i=d[i].nxt )
		{
			int v=d[i].to;
			if( d[i].flow&&!dis[v] )
			{
				dis[v]=dis[u]+1;
				if( v==t )	return true;
				q.push( v ); 
			}
		}
	}
	return false;
}
int dinic(int u,int flow)
{
	if( u==t )	return flow;
	int res=flow;
	for(int i=head[u];i;i=d[i].nxt)
	{
		int v=d[i].to;
		if( dis[v]==dis[u]+1&&d[i].flow )
		{
			int temp=dinic(v,min(res,d[i].flow) );
			if( temp==0 )	dis[v]=0;
			res-=temp;
			d[i].flow-=temp;
			d[i^1].flow+=temp;
		}
		if( res==0 )	break;
	}
	return flow-res;
}
int main()
{
	cin >> m >> n;
	s=n+m+1,t=s+1;
	for(int i=1,x;i<=m;i++ )
	{
		scanf("%d",&x);
		add( s,i,x );
		add( i,s,0 );
		sumn+=x;
		while(~(x = read())) add(i, m + x, inf),add(m+x,i,0);
	}
	for(int i=1,x; i<=n; i++)
	{
		scanf("%d",&x);
		add(i+m,t,x); add(t,i+m,0);
	}
	while( bfs() )
		maxflow+=dinic( s,inf );
	for(int i=1;i<=m;i++)
		if( dis[i] )	printf("%d ",i);
	puts("");
	for(int i=1;i<=n;i++)
		if( dis[i+m] )	printf("%d ",i);
	printf("\n%d",sumn-maxflow);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值