地址:http://acm.hdu.edu.cn/showproblem.php?pid=3810
题意:dota里面敌法师有一个技能blink,就是瞬间移动,而且没有CD。给定很多地方,有些距离比较近的地方是可以相互瞬间移动到的,问能否在达到给定金钱,并求出最小时间。
mark:本题把能够相互到达的可以看成一部分,那么就分成很多部分,每个部分没有任何联系,相互做一次01背包,取最小值就好的。
但是问题的关键是本题数据很大,背包容量最大是10亿,时间虽然是30s,但是直接扫肯定是不行的,本来我们是可以用队列的方式解普通的01背包,但是我们用队列跟直接用数组花费空间是一样大的,所以我们需要想办法剪枝,比如说时间都已经超过了中间临时的最小时间的,肯定不用继续搜下去了,还有当前容量占用小,时间花费长的肯定要剪掉,那么一个好的方法是优先队列,他可以很方便的剪掉刚才说的第二种无用状态。
故可用两个优先队列,类似与滚动数组的思路,第一个存放当前状态,第二个存放能够转移得到的状态,然后再把转移得到的状态剪掉一部分后放回第一个队列。
代码:
#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <algorithm> #include <queue> #include <vector> #define LL long long using namespace std; const int INF = 1000000000; const int N = 51; struct node { int t,g; bool operator<(const node& tt) const { if(g == tt.g) return t > tt.t; return g < tt.g; } }s[N],now,next; int n,m,tot,min1; int t[N],g[N],vst[N],num[N]; int adj[N][N]; int d[N][N]; priority_queue<node> q1,q2; void dfs(int i) { for(int j = 1; j <= n; j++) { if(vst[j]) continue; if(adj[i][j]) { vst[j] = 1; d[tot][num[tot]++] = j; dfs(j); } } } void init() { tot = 0; for(int i = 1; i <= n; i++) { if(!vst[i]) { vst[i] = 1; d[tot][num[tot]++] = i; dfs(i); tot++; } } } void solve() { int i,j,k; min1 = INF; for(i = 0; i < tot; i++) { while(!q1.empty()) q1.pop(); while(!q2.empty()) q2.pop(); now.t = now.g = 0; q1.push(now); for(j = 0; j < num[i]; j++) { while(!q1.empty()) { now = q1.top(); q1.pop(); q2.push(now); next.t = now.t+s[d[i][j]].t; next.g = now.g+s[d[i][j]].g; if(next.g >= m) {min1 = min(min1, next.t); continue;} if(next.t < min1) q2.push(next); } int tem = INF; while(!q2.empty()) { now = q2.top(); q2.pop(); if(tem >= now.t) { q1.push(now); tem = now.t; } } } } } int main() { int T; int i,j,k; scanf("%d", &T); for(int cc = 1; cc <= T; cc++) { scanf("%d%d", &n, &m); memset(adj, 0, sizeof(adj)); memset(num, 0, sizeof(num)); memset(vst, 0, sizeof(vst)); for(i = 1; i <= n; i++) { scanf("%d%d%d", &s[i].t, &s[i].g, &k); for(j = 0; j < k; j++) { int aa; scanf("%d", &aa); adj[i][aa] = adj[aa][i] = 1; } } printf("Case %d: ", cc); init(); solve(); if(min1 == INF) puts("Poor Magina, you can't save the world all the time!"); else printf("%d\n", min1); } return 0; }