hdu 3810 DP

原文转自:http://blog.csdn.net/woshi250hua/article/details/7609293

题目大意题目源自Dota,前面一堆介绍敌法师,最后一段才开始说有n堆野兽,每堆野兽屠杀完完需要花费ti时间,可以增加金钱gi,敌法师有瞬移技能,可以从某堆野兽移到另一堆野兽,题目有给定从哪堆可以移到哪堆。最后问在满足打的金钱多余m的情况下的最少时间。


解题思路2011年武汉邀请赛网络赛的金牌题,题目看上去很复杂,又是n堆野兽,又是各种瞬移。但是理清楚之后就发现这是题背包题,先把能够相连的野兽堆用深搜找出来归为一组,然后我们要做的就是在这组里面选择0,1,...num[tot]堆进行屠杀,每堆野兽有两种选择:杀获不杀.这样问题就转为求tot组01背包。

     但本题的金钱和屠杀时间范围都特别大,屠杀时间的范围是1-1000万,金钱的范围是1-10亿,不能用常规的两重循环模拟。其实用两个优先队列就可以模拟01背包的计算过程,第一次将(0,0)入队,然后出队转移到下一个状态,转移完这两个状态都进入队列,可以用两个优先队列进行操作,一个表示上一轮的计算结果,一个是这一轮,就相当于滚动数组。在用优先队列模拟的过程中要注意剪枝,如果不剪或者剪得不好就是MLE。我试了好几种剪枝,只有一种可以,那就是在每次转移完要把队列1复制到队列2的时候,判断下入队的这个点是不是满足价值比前面入队的点金钱少且耗时少,如果不是,这个肯定比如前面入队的那个优。比如,(6,4)表示打到钱为6,且耗时4,如果下一个要进队的是(5,5),那肯定是前面的更优,对吧?就是这样。

 

AC代码如下:

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
using namespace std;

const __int64 MAXN = 1000000000;

struct Node{
	__int64 val, t;
	friend int operator < ( Node a, Node b){
		if( a.val == b.val ){
			return a.t > b.t;
		}else{
			return a.val < b.val;
		}
	}
};

bool map[51][51], visit[51];
int group[51][51], gtot, num[51];
int N, M;
Node arr[51];
priority_queue<Node> qu1, qu2;

void DFS( int i ){
	visit[i] = true;
	int j = ++num[gtot];
	group[gtot][j] = i;
	for( int k = 1; k <= N; k++ ){
		if( !visit[k] && map[i][k] ){
			DFS( k );
		}
	}
}

void DivideGroup(){
	int i;
	for( gtot = i = 1; i <= N; i++ ){
		if( !visit[i] ){
			DFS( i );
			gtot++;
		}
	}
}

__int64 DP(){
	__int64 minn;
	minn = MAXN;
	for( int i = 1; i < gtot; i++ ){
		while( !qu1.empty() ){
			qu1.pop();
		}
		while( !qu2.empty() ){
			qu2.pop();
		}
		Node now;
		now.t = now.val = 0;
		qu1.push( now );

		for( int j = 1; j <= num[i]; j++ ){
			int k = group[i][j];
			while( !qu1.empty() ){
				now = qu1.top();
				qu1.pop();
				qu2.push( now );
				Node next = now;
				next.t += arr[k].t;
				next.val += arr[k].val;

				if( next.val >= M ){
					if( next.t < minn ){
						minn = next.t;
					}
					continue;
				}
				if( next.t >= minn ){
					continue;
				}
				qu2.push( next );
			}
			__int64 prev = MAXN;
			while( !qu2.empty() ){
				now = qu2.top();
				qu2.pop();
				if( prev >= now.t ){
					qu1.push( now );
					prev = now.t;
				}
			}
		}
	}
	return ( minn == MAXN ? -1 : minn );
}

int main(){
	int T, Case = 1;
	cin >> T;
	while( T-- ){
		cin >> N >> M;
		memset( map, false, sizeof( map ) );
		memset( visit, false, sizeof( visit ) );
		memset( num, 0, sizeof( num ) );
		for( int i = 1; i <= N; i++ ){
			int temp;
			cin >> arr[i].t >> arr[i].val >> temp;
			for( int j = 0; j < temp; j++ ){
				int temp1;
				cin >> temp1;
				map[i][temp1] = true;
			}
		}
		DivideGroup();
		__int64 ans = DP();
		if( ans == -1 ){
			printf( "Case %d: Poor Magina, you can't save the world all the time!\n", Case++ );
		}else{
			printf( "Case %d: %I64d\n", Case++, ans );
		}
	}
	return 0;
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值