POJ2125Destroying The Graph(二分图最小割可行边)

我发现很多人关于输出方案那个地方解释都是错的,这不是误导别人吗!!!

能ac又怎样啊…

还有人说什么因为左边没满流,所以右边满流…

第一问,求最小代价

由 于 最 后 要 把 每 条 边 都 去 掉 由于最后要把每条边都去掉

去 掉 一 条 a − b 的 边 , 要 么 把 a 的 出 边 去 掉 , 要 么 把 b 的 入 边 去 掉 去掉一条a-b的边,要么把a的出边去掉,要么把b的入边去掉 ab,a,b

可 以 发 现 这 不 就 是 二 分 图 吗 ? ? ! ! 可以发现这不就是二分图吗??!! ??!!

我 们 把 每 个 点 拆 分 为 出 点 ( 边 从 这 个 点 出 去 ) 和 入 点 ( 边 从 这 个 点 进 来 ) 我们把每个点拆分为出点(边从这个点出去)和入点(边从这个点进来) ()()

出 点 放 在 二 分 图 左 边 , 和 源 点 连 一 条 代 价 为 b i 的 边 出点放在二分图左边,和源点连一条代价为b_i的边 ,bi

入 点 放 在 二 分 图 右 边 , 和 汇 点 连 一 条 代 价 为 a i 的 边 入点放在二分图右边,和汇点连一条代价为a_i的边 ,ai

对 于 a − b 边 , a 的 出 点 和 b 的 入 点 连 一 条 代 价 无 穷 的 边 ( 保 证 最 小 割 不 会 割 这 条 边 ) 对于a-b边,a的出点和b的入点连一条代价无穷的边(保证最小割不会割这条边) ab,ab()

跑 最 大 流 ( 最 小 割 ) 就 是 答 案 跑最大流(最小割)就是答案 ()

为 什 么 ? \color{Red}为什么? ?

最 小 割 保 证 s 不 能 到 t 最小割保证s不能到t st

所 以 每 条 边 a − b 要 么 是 源 点 和 a 的 出 点 的 边 被 割 掉 所以每条边a-b要么是源点和a的出点的边被割掉 aba

要 么 是 b 的 入 点 和 汇 点 的 边 被 割 掉 ( 如 果 都 不 被 割 , 那 么 源 点 能 到 汇 点 , 不 是 最 小 割 ) 要么是b的入点和汇点的边被割掉(如果都不被割,那么源点能到汇点,不是最小割) b(,,)

这 就 符 合 所 有 边 都 会 被 割 掉 , 因 为 是 最 小 割 , 所 以 代 价 最 小 这就符合所有边都会被割掉,因为是最小割,所以代价最小 ,,

第二问

你 可 能 会 想 到 满 流 的 边 一 定 是 割 你可能会想到满流的边一定是割

但 满 流 的 边 只 是 割 边 的 必 要 条 件 但满流的边只是割边的必要条件

最小割的可行边和必须边

一 条 边 是 割 边 , 满 足 满 流 且 左 右 端 点 互 相 不 可 达 到 一条边是割边,满足满流且左右端点互相不可达到 ,

所 以 从 源 点 d f s , 只 走 没 有 满 流 的 边 ( 标 记 能 到 的 点 ) 所以从源点dfs,只走没有满流的边(标记能到的点) dfs,()

由 于 s 能 到 的 点 都 在 s 集 合 中 , 到 不 了 的 点 都 在 t 集 合 中 由于s能到的点都在s集合中,到不了的点都在t集合中 ss,t

所 以 枚 举 每 条 边 判 断 即 可 所以枚举每条边判断即可

#include <iostream>
#include <queue>
using namespace std;
const int maxn=2e5+10;
const int inf=1e9;
int n,m,s,t,sumn,dis[maxn],a[maxn],b[maxn],id[maxn];
struct edge{
	int to,nxt,flow;
}d[maxn]; int head[maxn],cnt=1;
void add(int u,int v,int flow){
	d[++cnt]=(edge){v,head[u],flow},head[u]=cnt;
	d[++cnt]=(edge){u,head[v],0},head[v]=cnt;
}
bool bfs()
{
	for(int i=0;i<=t;i++)	dis[i]=0;
	dis[s]=1;
	queue<int>q; 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]==0 )
			{
				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&&res;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;
		}
	}
	return flow-res;
}
vector<int>vec;
int ok[maxn];
void dfs(int u)
{
	for(int i=head[u];i;i=d[i].nxt )
	{
		int v=d[i].to;
		if( ok[v]||d[i].flow==0 )	continue;
		ok[v]=1; dfs(v);
	}
}
int main()
{
	cin >> n >> m;
	s=0,t=n+n+1;
	for(int i=1;i<=n;i++)	cin >> a[i];
	for(int i=1;i<=n;i++)	cin >> b[i];
	for(int i=1;i<=m;i++)
	{
		int l,r; cin >> l >> r;
		add(l+n,r,inf);//l+n是出边,r是入边 
	}
	for(int i=1;i<=n;i++)
	{
		add(s,i+n,b[i]);//出边被割掉,需要获得b[i]元
		add(i,t,a[i] );//入边被割掉 
	}
	int ans=0,shu=0;
	while( bfs() )	ans+=dinic(s,inf);
	cout << ans << '\n';
	dfs(s);	ok[s]=1;
	for(int i=1;i<=n;i++)
	{
		if( ok[s]!=ok[i+n] )	shu++;
		if( ok[t]!=ok[i] )	shu++;
	}
	cout << shu << '\n';
	for(int i=1;i<=n;i++)
	{
		if( ok[s]!=ok[i+n] )	cout << i << " -\n";
		if( ok[t]!=ok[i] )	cout << i << " +\n";
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值