网络流

最大流ISAP模板

poj1273

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
#define ll __int64
using namespace std ;

const int maxn = 111111 ;

struct Edge
{
	int from , to , cap , flow , next ;
} edge[maxn<<1] ;

int head[maxn] , tot , s , t , n , m ;
int dis[maxn] , vis[maxn] , num[maxn] , p[maxn] , cur[maxn] ;
queue<int> Q ;

int max ( int a , int b ) { return a > b ? a : b ; }
int min ( int a , int b ) { return a < b ? a : b ; }

void new_edge ( int a , int b , int c )
{
	edge[tot].to = b ;
	edge[tot].from = a ;
	edge[tot].cap = c ;
	edge[tot].flow = 0 ;
	edge[tot].next = head[a] ;
	head[a] = tot ++ ;
}

void bfs ()
{
	Q.push ( t ) ;
	vis[t] = 1 ;
	while ( !Q.empty () )
	{
		int u = Q.front () ;
		Q.pop () ;
		int i ;
		for ( i = head[u] ; i != -1 ; i = edge[i].next )
		{
			Edge e = edge[i] ;
			if ( !e.cap && !vis[e.to] )
			{
				vis[e.to] = 1 ;
				dis[e.to] = dis[u] + 1 ;
				Q.push ( e.to ) ;
			}
		}
	}
}

int arg ()
{
	int x = t , ret = 111111111 ;
	while ( x != s )
	{
		ret = min ( ret , edge[p[x]].cap - edge[p[x]].flow ) ;
		x = edge[p[x]].from ;
	}
	x = t ;
	while ( x != s )
	{
		edge[p[x]].flow += ret ;
		edge[p[x]^1].flow -= ret ;
		x = edge[p[x]].from ;
	}
	return ret ;
}

ll max_flow ()
{
	ll flow = 0 ;
	int x = s , i ;
	bfs () ;
	for ( i = 1 ; i <= n ; i ++ ) num[dis[i]] ++ , cur[i] = head[i] ;
	while ( dis[s] < n )
	{
		if ( x == t )
		{
			flow += (ll) arg () ;
			x = s ;
		}
		int ok = 0 ;
		for ( i = cur[x] ; i != -1 ; i = edge[i].next )
		{
			Edge e = edge[i] ;
			if ( e.cap > e.flow && dis[x] > dis[e.to] )
			{
				ok = 1 ;
				cur[x] = i ;
				p[e.to] = i ;
				x = e.to ;
				break ;
			}
		}
		if ( !ok )
		{
			int m = n - 1 , i ;
			for ( i = head[x] ; i != -1 ; i = edge[i].next )
			{
				Edge e = edge[i] ;
				if ( e.cap > e.flow ) m = min ( m , dis[e.to] ) ;
			}
			if ( --num[dis[x]] == 0 ) break ;
			num[dis[x]=m+1] ++ ;
			cur[x] = head[x] ;
			if ( x != s ) x = edge[p[x]].from ;
		}
	}
	return flow ;
}

void init ( int n )
{
	int i ;
	for ( i = 0 ; i <= n ; i ++ )
	{
		cur[i] = dis[i] = vis[i] = num[i] = 0 ;
		head[i] = -1 ;
	}
	tot = 0 ;
	while ( !Q.empty () ) Q.pop () ;
}

int main ()
{
	int a , b , c ;
	while ( scanf ( "%d%d" , &m , &n ) != EOF )
	{
		init ( n ) ;
		s = 1 , t = n ;
		while ( m -- )
		{
			scanf ( "%d%d%d" , &a , &b , &c ) ;
			new_edge ( a , b , c ) ;
			new_edge ( b , a , 0 ) ;
		}
		printf ( "%I64d\n" , max_flow () ) ;
	}
}

poj1149 PIGS

题意:囧king有M个猪圈,猪圈i中有num[i]头猪,但很囧的是,囧king没钥匙,钥匙都在顾客那里。之后n天中,每天会来一个顾客,他会打开其中的k个猪圈,从中挑出a头猪,挑完后,k个猪圈中的猪可以被重新安排,然后猪圈的们都会被关上,问囧king最多能卖出多少猪。

解题思路:囧king说是最大流。。i从1到n,建边(s,i )其容量是所有i能开的猪圈,但还没被其他人开过的猪圈里的猪的总和。若有已经被开过的猪圈,设上一次开这个猪圈的人为x,则建边(x,i),容量为INF,并将上一次开这个猪圈的人更新为i。最后从1到n,建边(i,t)容量为i的购买力。其原理可以理解为,能直接流到i的猪为未被开过的猪圈里的猪,而若有猪圈已经被开过了,那能流到上一个人的猪全都可以流到i这里。最后从i这个人只能流出他的购买力。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std ;

const int INF = INT_MAX >> 1 ;
const int maxn = 211111 ;

int max ( int a , int b ) { return a > b ? a : b ; }
int min ( int a , int b ) { return a < b ? a : b ; }

struct Edge
{
	int from , to , cap , flow , next ;
} edge[maxn<<1] ;
int head[maxn] , tot ;
int s , t , n ;
void new_edge ( int a , int b , int c )
{
	edge[tot].from = a ;
	edge[tot].to = b ;
	edge[tot].cap = c ;
	edge[tot].flow = 0 ;
	edge[tot].next = head[a] ;
	head[a] = tot ++ ;
}

struct ISAP
{
	int cur[maxn] , dis[maxn] , p[maxn] , num[maxn] , vis[maxn] ;
	queue<int> Q ;

	void bfs ()
	{
		while ( !Q.empty () ) Q.pop () ;
		int u , v , i ;
		Q.push ( t ) ;
		vis[t] = 1 ;
        while ( !Q.empty () )
		{
			u = Q.front () , Q.pop () ;
			for ( i = head[u] ; i != -1 ; i = edge[i].next )
			{
				int v = edge[i].to ;
				if ( edge[i].cap == 0 && !vis[v] )
				{
					Q.push ( v ) ;
					dis[v] = dis[u] + 1 ;
					vis[v] = 1 ;
				}
			}
		}
	}

	int arg ()
	{
		int ret = INF , x = t ;
		while ( x != s )
		{
			ret = min ( ret , edge[p[x]].cap - edge[p[x]].flow ) ;
			x = edge[p[x]].from ;
		}
		x = t ;
		while ( x != s )
		{
			edge[p[x]].flow += ret ;
			edge[p[x]^1].flow -= ret ;
			x = edge[p[x]].from ;
		}
		return ret ;
	}

	int max_flow ()
	{
		bfs () ;
		int i , x = s , flow = 0 ;
		for ( i = 1 ; i <= n ; i ++ ) num[dis[i]] ++ , cur[i] = head[i] ;
		while ( dis[s] < n )
		{
			if ( x == t )
			{
				flow += arg () ;
				x = s ;
			}
			int ok = 0 ;
			for ( i = cur[x] ; i != -1 ; i = edge[i].next )
			{
				Edge e = edge[i] ;
				if ( e.cap > e.flow && dis[e.to] < dis[x] )
				{
					ok = 1 , cur[x] = i , p[e.to] = i , x = e.to ;
					break ;
				}
			}
			if ( !ok )
			{
				int m = n - 1 ;
				for ( i = head[x] ; i != -1 ; i = edge[i].next )
				{
					if ( edge[i].cap > edge[i].flow )
						m = min ( m , dis[edge[i].to] ) ;
				}
				if ( --num[dis[x]] == 0 ) break ;
				num[dis[x]=m+1] ++ ;
				cur[x] = head[x] ;
				if ( x != s ) x = edge[p[x]].from ;
			}
		}
		return flow ;
	}

	void init ()
	{
		int i ;
		for ( i = 0 ; i <= n ; i ++ )
		{
			dis[i] = num[i] = vis[i] = 0 ;
			head[i] = -1 ;
		}
		tot = 0 ;
	}

} sap ;

int num[maxn] , to[maxn] ;
int main ()
{
	int i , a , b , c , j , m ;
	while ( scanf ( "%d%d" , &m , &n ) != EOF )
	{
		n += 2 ;
		sap.init () ;
		s = n , t = n - 1 ;
		for ( i = 1 ; i <= m ; i ++ ) scanf ( "%d" , &num[i] ) , to[i] = 0 ;
		for ( i = 1 ; i <= n - 2 ; i ++ )
		{
			int k ;
			scanf ( "%d" , &k ) ;
			int add = 0 ;
			while ( k -- )
			{
				scanf ( "%d" , &a ) ;
				if ( to[a] == 0 ) 
			    {
					add += num[a] ;
					to[a] = i ;
				}
				else
				{
					int x = to[a] ;
					to[a] = i ;
					new_edge ( x , i , INF ) ;
					new_edge ( i , x , 0 ) ;
				}
			}
			if ( add ) new_edge ( s , i , add ) , new_edge ( i , s , 0 ) ;
			scanf ( "%d" , &a ) ;
			if ( a ) new_edge ( i , t , a ) , new_edge ( t , i , 0 ) ;
		}
		printf ( "%d\n" , sap.max_flow () ) ;
	}
}
/*
3 3
3 1 3
2 1 2 1
2 2 3 1
1 2 5
ans = 7
*/


uva11082 Matrix Decompressing

题意:给出一个矩形的长宽,每一行的和,每一列的和(题目并不是直接给出,稍微转换下),还原出该矩形,矩阵上的任一元素大于等于1,小于等于20。题目保证至少有一个解,输出任一解。

解题思路:最大流。。每一行,每一列分别为一个节点,再设一个起点,一个终点。起点向行节点建边,容量为该行的和减去列数(保证每个元素至少为1,即图上已经有(m*n)的流量流过),列节点向终点建边,容量为该列的和减去行数(也是为了保证每个元素至少为1)。行节点向列节点建边,容量为19(因为每条边上已经有1的流量流过)。最大流流一遍后,行节点指向列节点的边上的流量+1即为矩阵上每个元素的值。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std ;

const int maxn = 111111 ;
int min ( int a , int b ) { return a < b ? a : b ;}

struct Edge
{
	int from , to , cap , flow , next ;
} edge[maxn] ;
int head[maxn] , tot , s , t , n ;

void new_edge ( int a , int b , int c )
{
	edge[tot].from = a ;
	edge[tot].to = b ;
	edge[tot].cap = c ;
	edge[tot].flow = 0 ;
	edge[tot].next = head[a] ;
	head[a] = tot ++ ;
}

struct ISAP
{
	int vis[maxn] , p[maxn] , num[maxn] , dis[maxn] , cur[maxn] ;
	queue<int> Q ;

	void init ( int n )
	{
		while ( !Q.empty () ) Q.pop () ;
		int i ;
		for ( i = 0 ; i <= n ; i ++ )
		{
			vis[i] = num[i] = dis[i] = 0 ;
			head[i] = -1 ;
		}
		tot = 0 ;
	}

	void bfs ()
	{
		int u , v , i ;
		Q.push ( t ) ;
		vis[t] = 1 ;
		while ( !Q.empty () )
		{
			u = Q.front () , Q.pop () ;
			for ( i = head[u] ; i != -1 ; i = edge[i].next )
				if ( !edge[i].cap && !vis[edge[i].to] )
				{
					Q.push ( edge[i].to ) , vis[edge[i].to] = 1 ;
					dis[edge[i].to] = dis[u] + 1 ;
				}
		}
	}

	int arg ()
	{
		int ret = 111111 , x = t ;
		while ( x != s )
		{
			ret = min ( ret , edge[p[x]].cap - edge[p[x]].flow ) ;
			x = edge[p[x]].from ;
		}
		x = t ;
		while ( x != s )
		{
			edge[p[x]].flow += ret ;
			edge[p[x]^1].flow -= ret ;
			x = edge[p[x]].from ;
		}
		return ret ;
	}

	int max_flow ()
	{
		bfs () ;
		int x = s , i , flow = 0 ;
		for ( i = 1 ; i <= n ; i ++ ) num[dis[i]] ++ , cur[i] = head[i] ;
		while ( dis[s] < n )
		{
			if ( x == t ) x = s , flow += arg () ;
			int ok = 0 ;
			for ( i = cur[x] ; i != -1 ; i = edge[i].next )
			{
				Edge e = edge[i] ;
				if ( e.cap > e.flow && dis[e.to] < dis[x] )
				{
					ok = 1 , cur[x] = i , p[e.to] = i , x = e.to ;
					break ;
				}
			}
			if ( !ok )
			{
				int m = n - 1 ;
				for ( i = head[x] ; i != -1 ; i = edge[i].next )
					if ( edge[i].cap > edge[i].flow )
						m = min ( m , dis[edge[i].to]) ;
				if ( -- num[dis[x]] == 0 ) break ;
				num[dis[x]=m+1] ++ ;
				cur[x] = head[x] ;
				if ( x != s ) x = edge[p[x]].from ;
			}
		}
		return flow ;
	}

} sap;

int ret[888][888] , num[888] ;

int main ()
{
	int m , i , j , k ;
	int ca = 0 , cas ;
	scanf ( "%d" , &cas ) ;
	while (  cas -- )
	{  
		scanf ( "%d%d" , &m , &n ) ;
		n += m + 2 ;
		sap.init ( n ) ;
		s = n - 1 , t = n ;

		for ( i = 1 ; i <= m ; i ++ ) scanf ( "%d" , &num[i] ) ;
		for ( i = m ; i >= 1 ; i -- ) num[i] -= ( ( n - m - 2 ) + ( i == 1 ? 0 : num[i-1] ) ) ;
		for ( i = m + 1 ; i <= n - 2 ; i ++ ) scanf ( "%d" , &num[i] ) ;
		for ( i = n - 2 ; i >= m + 1 ; i -- ) num[i] -= ( m + ( i == m + 1 ? 0 : num[i-1] ) ) ;

		for ( i = 1 ; i <= m ; i ++ )
		{
			new_edge ( s , i , num[i] ) ;
			new_edge ( i , s , 0 ) ;
		}
		for ( i = m + 1 ; i <= n - 2 ; i ++ )
		{
			new_edge ( i , t , num[i] ) ;
			new_edge ( t , i , 0 ) ;
		}
		for ( i = 1 ; i <= m ; i ++ )
			for ( j = m + 1 ; j <= n - 2 ; j ++ )
			{
				new_edge ( i , j , 19 ) ;
				new_edge ( j , i , 0 ) ;
			}
		sap.max_flow () ;
		for ( i = 1 ; i <= m ; i ++ )
			for ( j = head[i] ; j != -1 ; j = edge[j].next )
				if ( edge[j].cap ) ret[i][edge[j].to] = edge[j].flow + 1 ;
		printf ( "Matrix %d\n" , ++ca ) ;
		for ( i = 1 ; i <= m ; i ++ )
		{
			for ( j = m + 1 ; j <= n - 2 ; j ++ )
			{
				if ( j != m + 1 ) putchar ( ' ' ) ;
				printf ( "%d" , ret[i][j] ) ;
			}
			puts ( "" ) ;
		}
	}
}

uva11167 - Monkeys in the Emei Mountain


题意:有n只猴子要喝水,每只猴子只能在(a,b)时间段内喝v单位的水,不能多也不能少,也不能再其他时间段喝,每单位时间喝一单位水。而水源在同一单位时间能只能供m只猴子喝,问能否满足猴子们的要求,若能,输出每只猴子喝水的时间段。

解题思路:最大流。。可以想到暴力的建图方法,每个单位时间为一个节点,每只猴子也为一个节点,那么每个时间节点向它能供给的猴子建一条边,容量为1。源点向时间节点建边,容量为m。猴子向汇点建边,容量为v[i]。图上流过的最大流即为所有猴子所能喝的最多的水,若最大流等于猴子需求的水的总量,则能满足要求,否则不能(因为猴子向终点建的边的上限为猴子所能喝的水)。时间节点流向猴子的边上的流量即为该时间点该猴子喝的水(为0则不喝,为1则喝)。这是暴力的求解方法,时间复杂度太高,不可取。但这幅图还可以优化,我们并不需要每个时间点建一个节点,而只需要将猴子喝水的时间的交叉段全都分开来建节点,每一段一个节点。设时间段i的间隔为l[i],源点向时间段节点建边,容量为l[i] * m , 时间段节点向能在该时间段喝水的猴子建边,容量为l[i]。每只猴子向汇点建边,容量为v[i]。最大流流一遍后,是否有解同上。把每个时间段流量猴子的流量都取出来,暴力寻找猴子的喝水时间段即可。

#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<queue>
using namespace std ;

const int maxn = 111111 ;
int min ( int a , int b ) { return a < b ? a : b ;}

struct Edge
{
	int from , to , cap , flow , next ;
} edge[maxn] ;
int head[maxn] , tot , s , t , n ;

void new_edge ( int a , int b , int c )
{
	edge[tot].from = a ;
	edge[tot].to = b ;
	edge[tot].cap = c ;
	edge[tot].flow = 0 ;
	edge[tot].next = head[a] ;
	head[a] = tot ++ ;
}

struct ISAP
{
	int vis[maxn] , p[maxn] , num[maxn] , dis[maxn] , cur[maxn] ;
	queue<int> Q ;

	void init ()
	{
		while ( !Q.empty () ) Q.pop () ;
		int i ;
		for ( i = 0 ; i <= n ; i ++ )
		{
			vis[i] = num[i] = dis[i] = 0 ;
			head[i] = -1 ;
		}
		tot = 0 ;
	}

	void bfs ()
	{
		int u , v , i ;
		Q.push ( t ) ;
		vis[t] = 1 ;
		while ( !Q.empty () )
		{
			u = Q.front () , Q.pop () ;
			for ( i = head[u] ; i != -1 ; i = edge[i].next )
				if ( !edge[i].cap && !vis[edge[i].to] )
				{
					Q.push ( edge[i].to ) , vis[edge[i].to] = 1 ;
					dis[edge[i].to] = dis[u] + 1 ;
				}
		}
	}

	int arg ()
	{
		int ret = 111111 , x = t ;
		while ( x != s )
		{
			ret = min ( ret , edge[p[x]].cap - edge[p[x]].flow ) ;
			x = edge[p[x]].from ;
		}
		x = t ;
		while ( x != s )
		{
			edge[p[x]].flow += ret ;
			edge[p[x]^1].flow -= ret ;
			x = edge[p[x]].from ;
		}
		return ret ;
	}

	int max_flow ()
	{
		bfs () ;
		int x = s , i , flow = 0 ;
		for ( i = 1 ; i <= n ; i ++ ) num[dis[i]] ++ , cur[i] = head[i] ;
		while ( dis[s] < n )
		{
			if ( x == t ) x = s , flow += arg () ;
			int ok = 0 ;
			for ( i = cur[x] ; i != -1 ; i = edge[i].next )
			{
				Edge e = edge[i] ;
				if ( e.cap > e.flow && dis[e.to] < dis[x] )
				{
					ok = 1 , cur[x] = i , p[e.to] = i , x = e.to ;
					break ;
				}
			}
			if ( !ok )
			{
				int m = n - 1 ;
				for ( i = head[x] ; i != -1 ; i = edge[i].next )
					if ( edge[i].cap > edge[i].flow )
						m = min ( m , dis[edge[i].to]) ;
				if ( -- num[dis[x]] == 0 ) break ;
				num[dis[x]=m+1] ++ ;
				cur[x] = head[x] ;
				if ( x != s ) x = edge[p[x]].from ;
			}
		}
		return flow ;
	}

} sap;

struct Monkey
{
	int a , b , v ;
} mon[maxn] ;

struct Ans
{
	int a , b ;
	bool operator < ( const Ans &p ) const
	{
		return a < p.a ;
	}
} ;
vector<Ans> vec[maxn] ;
int x[1111] , y[1111] ;
int num[maxn] ;
int main ()
{
	int m , i , j , k , sum , ca = 0 ;
	while ( scanf ( "%d" , &n ) != EOF )
	{
		if ( n == 0 ) break ;
		k = n ;
		sum = 0 ;
		int T = 0 ;
		scanf ( "%d" , &m ) ;
		for ( i = 1 ; i <= n ; i ++ )
		{
			scanf ( "%d%d%d" , &mon[i].v , &mon[i].a , &mon[i].b ) ;
			num[++T] = mon[i].a ;
			num[++T] = mon[i].b ;
			sum += mon[i].v ;
		}
		sort ( num + 1 , num + T + 1 ) ;
		T = unique ( num + 1 , num + T + 1 ) - num - 1 ;
		sort ( num + 1 , num + T + 1 ) ;
		n = T + 2 + n ;
		s = n - 1 , t = n ;
		sap.init () ;
		for ( i = 2 ; i <= T ; i ++ )
		{
			new_edge ( s , i , m * ( num[i] - num[i-1] ) ) ;
			new_edge ( i , s , 0 ) ;
			for ( j = 1 ; j <= k ; j ++ )
			{
				if ( mon[j].a <= num[i-1] && mon[j].b >= num[i] )
				{
					new_edge ( i , T + j , num[i] - num[i-1] ) ;
					new_edge ( T + j , i , 0 ) ;
				}
			}
		}
		for ( i = 1 ; i <= k ; i ++ )
		{
			new_edge ( i + T , t , mon[i].v ) ;
			new_edge ( t , i + T , 0 ) ;
		}
		int fuck = sap.max_flow () ;
		for ( i = 1 ; i <= n ; i ++ ) vec[i].clear () ;
		printf ( "Case %d: " , ++ ca ) ;
		if ( fuck < sum )
			puts ( "No" ) ;
		else
		{
			puts ( "Yes" ) ;
			for ( i = 2 ; i <= T ; i ++ )
			{
				int last = num[i-1] ;
				for ( j = head[i] ; j != -1 ; j = edge[j].next )
				{
					Edge e = edge[j] ;
					if ( e.flow > 0 )
					{
						Ans u , p ;
						int v = e.to ;
						if ( last + e.flow > num[i] )
						{
							u.a = last , u.b = num[i] ;
							vec[v].push_back ( u ) ;
							e.flow = ( last + e.flow - num[i] ) ;
							last = num[i-1] ;
						}
						u.a = last , u.b = last + e.flow ;
						last += e.flow ;
						vec[v].push_back ( u ) ;
						if ( last >= num[i] ) last = num[i-1] ;
					}
				}
			}
			for ( i = 1 ; i <= k ; i ++ )
			{
				sort ( vec[T+i].begin () , vec[T+i].end () ) ;
				int l = vec[T+i].size () ;
				int add = 0 ;
				x[++add] = vec[T+i][0].a , y[add] = vec[T+i][0].b ;
				for ( j = 1 ; j < l ; j ++ )
				{
					if ( vec[T+i][j].a == vec[T+i][j-1].b ) y[add] = vec[T+i][j].b ;
					else x[++add] = vec[T+i][j].a , y[add] = vec[T+i][j].b ;
				}
				printf ( "%d" , add ) ;
				for ( j = 1 ; j <= add ; j ++ )
					printf ( " (%d,%d)" , x[j] , y[j] ) ;
				puts ( "" ) ;
			}
		}
	}
}
/*
3 2
2 1 10
9 1 10
7 1 10
*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值