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

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

能ac又怎样啊…

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

第一问,求最小代价

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

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

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

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

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

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

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

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

? \color{Red}为什么?

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

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

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

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

第二问

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

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

最小割的可行边和必须边

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

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

s s , t 由于s能到的点都在s集合中,到不了的点都在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";
	}
}

猜你喜欢

转载自blog.csdn.net/jziwjxjd/article/details/108331772