【HDU】4862 Jump 费用流

传送门:【HDU】4862 Jump



题目大意:给你一个N行M列个格子的矩阵,矩阵每个格子中有一个值(0~9),一开始你有活力值为0,然后你可以进行最多K次游戏,每次可以任选矩阵中的一个点作为顶点,然后开始游戏,每次你可以选择从这个点跳到它的右边的点或者下边的点或者不动。每次跳跃,你将支付两个点的曼哈顿距离-1的活力值,能量值可以为负。如果一次跳跃的起点和终点的格子中的值相同,你的活力值可以增加这个值大小的值。每个格子最多只可以经过一次。每次游戏你可以跳任意多次,但不能违反规则。

现在你的任务是要在必须将所有的格子都跳过后能得到的最多的活力值。当然不必满足将K次机会全部用掉。



题目分析:

这次女队写出来的题目我没写出来,是该好好反思了,唉。。。

都怪平时不努力Q w Q

简单说一下这题吧,说实话其实也不难,可是当时却没去想。。

首先题目要求每个格子必须遍历一次,最多也只能遍历一次,那么可以把一个点拆成两个点i、i',建边(i,i',1,-x)中间的权值设为比所有点的权值和还大就行了(当然求的是最大费用流,权值取反,最大的意思就是最小),这样就能保证如果可以经过这个点,那么就会优先经过这个点。然后设立父子源点以及父子汇点,其中父源点向子源点输送K的流量(fas,sons,K,0),子源点向每个格子输送1的流量(sons,i,1,0),这样就能保证最多进行K次游戏了~。同理汇点一个方法处理一下。

然后每个格子向他能跳到的点建边(i’,j,1,-(-(曼哈顿距离-1)+a)),其中的a当两个格子相同时等于相同的那个值,否则等于0。

最后跑一次费用流即可,设得到的费用为cost,如果(-cost)/x != n*m,那么说明无解。

否则输出(-cost)%x。

最后提醒一下我错的地方:并不是一定要求得最小费用最大流的,因为可能只要小于K次游戏就能得到最优解的,但是如果要求最大流的话,那么可能会错失最优解,反而错误,所以最好的方法是要么cost不能再减小的时候就退出(为什么这样可行呢?因为每次都是最短路,所以每次得到的最短路大小都是递增的,所以一次不满足,之后的一定也不满足了)。当然还有别的方法可以避免这个问题了,我就不熬述了。


唉,果然还是太弱,努力努力再努力!!!!!!!!!!!!!!!!!


代码如下:


#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

#define REP( i , n ) for ( int i = 0 ; i < n ; ++ i )
#define REPF( i , a , b ) for ( int i = a ; i <= b ; ++ i )
#define REPV( i , a , b ) for ( int i = a ; i >= b ; -- i )
#define clear( a , x ) memset ( a , x , sizeof a )
#define copy( a , x ) memcpy ( a , x , sizeof a )

const int MAXN = 250 ;
const int MAXQ = 1000000 ;
const int MAXE = 1000000 ;
const int INF = 0x3f3f3f3f ;

struct Edge {
	int v , c , w , n ;
	Edge () {}
	Edge ( int var , int cost , int val , int next ) :
		v ( var ) , c ( cost ) , w ( val ) , n ( next ) {}
} ;

struct NetWork {
	Edge E[MAXE] ;
	int H[MAXN] , cntE ;
	int d[MAXN] , cur[MAXN] , a[MAXN] ;
	int Q[MAXQ] , head , tail , inq[MAXN] ;
	int s , t ;
	int S , T ;
	int cost , flow ;
	int n , m , K ;
	int G[MAXN][MAXN] ;
	
	void init () {
		cntE = 0 ;
		clear ( H , -1 ) ;
	}
	
	void addedge ( int u , int v , int c , int w ) {
		E[cntE] = Edge ( v , c ,  w , H[u] ) ;
		H[u] = cntE ++ ;
		E[cntE] = Edge ( u , 0 , -w , H[v] ) ;
		H[v] = cntE ++ ;
	}
	
	int spfa () {
		clear ( inq , 0 ) ;
		clear ( d , INF ) ;
		d[s] = 0 ;
		a[s] = INF ;
		cur[s] = -1 ;
		head = tail = 0 ;
		Q[tail ++] = s ;
		while ( head != tail ) {
			int u = Q[head ++] ;
			if ( head == MAXQ )
				head = 0 ;
			inq[u] = 0 ;
			for ( int i = H[u] ; ~i ; i = E[i].n ) {
				int v = E[i].v ;
				if ( E[i].c && d[v] > d[u] + E[i].w ) {
					d[v] = d[u] + E[i].w ;
					a[v] = min ( a[u] , E[i].c ) ;
					cur[v] = i ;
					if ( !inq[v] ) {
						inq[v] = 1 ;
						if ( d[v] < d[head] ) {
							Q[-- head] = v ;
							if ( head == 0 )
								head = MAXQ - 1 ;
						}
						else {
							Q[tail ++] = v ;
							if ( tail == MAXQ )
								tail = 0 ;
						}
					}
				}
			}
		}
		if ( d[t] == INF )
			return 0 ;
		if ( cost + d[t] * a[t] < cost )
			cost += d[t] * a[t] ;
		else
			return 0 ;
		flow += a[t];
		for ( int i = cur[t] ; ~i ; i = cur[E[i ^ 1].v] ) {
			E[i].c -= a[t] ;
			E[i ^ 1].c += a[t] ;
		}
		return 1 ;
	}
	
	int MCMF () {
		cost = flow = 0 ;
		while ( spfa () ) ;
		return -cost ;
	}
	
	void input () {
		scanf ( "%d%d%d" , &n , &m , &K ) ;
		int nm = n * m ;
		S = 2 * nm , T = S + 1 ;
		s = T + 1 , t = s + 1 ;
		addedge ( s , S , K , 0 ) ;
		addedge ( T , t , K , 0 ) ;
		REP ( i , n ) {
			REP ( j , m ) {
				scanf ( "%1d" , &G[i][j] ) ;
				addedge ( S , i * m + j , 1 , 0 ) ;
				addedge ( i * m + j , nm + i * m + j , 1 , -100000 ) ;
				addedge ( nm + i * m + j , T , 1 , 0 ) ;
			}
		}
		REP ( i , n )
			REP ( j , m )
				REPF ( k , 1 , 10 ) {
					if ( j + k < m )
						addedge ( nm + i * m + j , i * m + j + k , 1 , k - 1 - ( G[i][j] == G[i][j + k] ? G[i][j] : 0 ) ) ;
					if ( i + k < n )
						addedge ( nm + i * m + j , ( i + k ) * m + j , 1 , k - 1 - ( G[i][j] == G[i + k][j] ? G[i][j] : 0 ) ) ;
				}
	}
	
	void solve () {
		init () ;
		input () ;
		int ans = MCMF () ;
		if ( ans / 100000 != n * m )
			printf ( "-1\n" ) ;
		else
			printf ( "%d\n" , ans % 100000 ) ;
	}
} ;

NetWork nw ;

int main () {
	int T , cas ;
	for ( scanf ( "%d" , &T ) , cas = 1 ; cas <= T ; ++ cas ) {
		printf ( "Case %d : " , cas ) ;
		nw.solve () ;
	}
	return 0 ;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值