码迷,mamicode.com
首页 > 其他好文 > 详细

[CQOI2014]危桥

时间:2020-12-29 11:01:47      阅读:0      评论:0      收藏:0      [点我收藏+]

标签:for   return   int   clu   oid   else   target   反向   pen   

题目

点这里看题目。

分析

首先可以发现 " 往返 " 可以单纯考虑成 " 路径走两遍 " ,也不会影响正确性。那么一条危桥就只能走一次了

初看显然会以为是一个多源汇网络流问题。

此时就出现了两个问题:

  1. 有可能 \(a_1\rightarrow b_2\) 流了一些, \(b_1\rightarrow a_2\) 也可以流一些,那么就可能存在 " 虚假 " 的可行方案。
  2. 一条危桥可能正方向流一次,反方向流一次,结果就啥也没有了。

解决第一个问题的方法是,如果我们能让 \(a_1\) 也向 \(b_1\) 流一些,那么将 \(a_1\rightarrow b_2\) 的流反向之后我们就得到了一些可行流。

根据这个思路,我们可以尝试交换 \(b_1\)\(b_2\) ,再跑最大流。如果两次都满流,那么就说明绝对有解。

下面将说明正确性:

咕咕咕了

小结:

通过更换源汇、反向造流的思路值得借鉴。

代码

#include <cstdio>

const int INF = 0x3f3f3f3f;
const int MAXN = 1e4 + 5, MAXM = 1e5 + 5;

template<typename _T>
void read( _T &x )
{
	x = 0; char s = getchar(); int f = 1;
	while( s < ‘0‘ || ‘9‘ < s ) { f = 1; if( s == ‘-‘ ) f = -1; s = getchar(); }
	while( ‘0‘ <= s && s <= ‘9‘ ) { x = ( x << 3 ) + ( x << 1 ) + ( s - ‘0‘ ), s = getchar(); }
	x *= f;
}

template<typename _T>
void write( _T x )
{
	if( x < 0 ) putchar( ‘-‘ ), x = -x;
	if( 9 < x ) write( x / 10 );
	putchar( x % 10 + ‘0‘ );
}

template<typename _T>
_T MIN( const _T a, const _T b )
{
	return a < b ? a : b;
}

template<typename _T>
void swapp( _T &x, _T &y )
{
	_T t = x; x = y, y = t;
}

struct Edge
{
	int to, nxt, c;
}Graph[MAXM << 1];

char S[55][55];

int q[MAXN];
int head[MAXN], dep[MAXN], cur[MAXN];
int N, A1, A2, AN, B1, B2, BN, cnt = 1, tot;

void AddEdge( const int from, const int to, const int C ) 
{
	Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
	Graph[cnt].c = C, head[from] = cnt;
}

void AddE( const int from, const int to, const int C ) { AddEdge( from, to, C ), AddEdge( to, from, 0 ); }

bool BFS( const int S, const int T )
{
	int h = 1, t = 0, u, v;
	for( int i = 1 ; i <= tot ; i ++ ) dep[i] = INF;
	dep[q[++ t] = S] = 0;
	while( h <= t )
	{
		u = q[h ++];
		for( int i = head[u] ; i ; i = Graph[i].nxt )
			if( Graph[i].c && dep[v = Graph[i].to] > dep[u] + 1 )
				dep[q[++ t] = v] = dep[u] + 1;
	}
	return dep[T] < INF;
}

int DFS( const int u, const int lin, const int T )
{
	if( u == T ) return lin;
	int used = 0, ret, v, c;
	for( int &i = cur[u] ; i ; i = Graph[i].nxt )
	{
		v = Graph[i].to, c = Graph[i].c;
		if( dep[v] == dep[u] + 1 && c && ( ret = DFS( v, MIN( lin - used, c ), T ) ) )
		{
			used += ret, Graph[i].c -= ret, Graph[i ^ 1].c += ret;
			if( used == lin ) break;
		}
	}
	if( used < lin ) dep[u] = INF;
	return used;
}

int Dinic( const int S, const int T )
{
	int ret = 0;
	while( BFS( S, T ) )
	{
		for( int i = 1 ; i <= tot ; i ++ ) cur[i] = head[i];
		ret += DFS( S, INF, T );
	}
	return ret;
}

void Clean()
{
	cnt = 1, tot = N;
	for( int i = 1 ; i <= tot + 2 ; i ++ )
		head[i] = q[i] = dep[i] = cur[i] = 0;
}

bool Chk()
{
	Clean();
	const int s = ++ tot, t = ++ tot;
	for( int i = 1 ; i <= N ; i ++ )
		for( int j = 1 ; j <= N ; j ++ )
		{
			if( S[i][j] == ‘O‘ ) AddE( i, j, 1 );
			else if( S[i][j] == ‘N‘ ) AddE( i, j, INF );
		}
	AddE( s, A1, AN ), AddE( s, B1, BN );
	AddE( A2, t, AN ), AddE( B2, t, BN );
	int flow = Dinic( s, t );
	return flow == AN + BN;
}

int main()
{
	while( ~ scanf( "%d %d %d %d %d %d %d", &N, &A1, &A2, &AN, &B1, &B2, &BN ) )
	{
		A1 ++, A2 ++, B1 ++, B2 ++;
		for( int i = 1 ; i <= N ; i ++ ) scanf( "%s", S[i] + 1 );
		if( Chk() && ( swapp( B1, B2 ), Chk() ) ) puts( "Yes" );
		else puts( "No" );
	}
	return 0;
}

[CQOI2014]危桥

标签:for   return   int   clu   oid   else   target   反向   pen   

原文地址:https://www.cnblogs.com/crashed/p/14175788.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!