【HDU 3810】 Magina (01背包,优先队列优化,并查集)
Magina
Problem DescriptionMagina, also known as Anti-Mage, is a very cool hero in DotA (Defense of the Ancient).
If you have no idea of him, here is some brief introduction of his legendary antecedents:
Twin sons to the great Prophet, Terrorblade and Magina were blessed with divine powers: Terrorblade granted with an unnatural affinity with life forces; Magina gifted with energy manipulation. Magina's eventual overexposure to the magic gradually augmented his elemental resistances and bestowed him the unique ability to move faster than light itself. Now, broken by Terrorblade's fall to the dark side, Magina answers the Sentinel's call in a desperate bid to redeem his brother. Every bitter strike turns the Scourge's evil essences upon themselves, culminating in a finale that forces his enemy to awaken to the void within and spontaneously implode.
Magina has a very IMBA (imbalanced) skill – Blink, yes, move from one place to another place in a wink. Our problem begins at there.
As a formidable hero in the later stage, Magina always farm with the wild monsters for a long time. To make the farming more efficient, Magina use Blink frequently to jump here and there. Here we assume Blink skill has no CD, that is, we can use this skill at any time we want.
There are N spots of the wild monsters, and Magina can choose any one to begin. For every spots, Magina may use Ti time to kill the monsters and gain Gi units money, or he choose blink to other spots, which is known to our brilliant Magina. If the monsters in a spot were killed, it won’t appear any more.
Now Magina want to get M units money to but some excellent equipment, say Battle Fury for example. As a hero to save the world, there is no much time left for Magina, he wonders the minimum time for him to gain at least M units money.
InputThe first line contains a single integer T, indicating the number of test cases.
Each test case begins with two integers N, M. Their meanings are the same as the description.
Then N blocks follow, each one describes a spot of wild monsters.
The first line of each block is there integers Ti, Gi and Ki. Ti is the time, Gi is the units of money, Ki is the number of spots Magina can blink to from here.
Then Ki integer Cij follow, indicating the spots’ ID Magina can blink to. You may assume no ID would appear twice.
The spots are described with ID increasing from 1 to N. Input ensure if you can blink from i to j, you can also blink from j to i.
Technical Specification
1. 1 <= T <= 50
2. 1 <= N <= 50
3. 1 <= Ti <= 10000000
4. 1 <= M, Gi <= 1000000000
5. 1 <= Ki < N
6. 1 <= Cij <= N
OutputFor each test case, output the case number first, then the minimum time for Magina to gain at least M units money, if can’t, output “Poor Magina, you can't save the world all the time!”.
Sample Input31 42 5 01 51 4 04 101 9 03 3 13 3 32 2 44 4 13Sample OutputCase 1: 2Case 2: Poor Magina, you can't save the world all the time!Case 3: 10
【题意】
有n堆野兽,每堆野兽屠杀完完需要花费ti时间,可以增加金钱gi,敌法师有瞬移技能,可以从某堆野兽移到另一堆野兽,题目有给定从哪堆可以移到哪堆。最后问在满足打的金钱多余m的情况下的最少时间。
1 <= T <= 50 , 1 <= N <= 50 , 1 <= Ti <= 10000000 ,1 <= M, Gi <= 1000000000
【分析】
注意看数据范围,如果数据小的话,只是一道简单的01背包。
所以这是一道进化的01背包。
之前打01背包的时候就觉得有很多状态其实是没有用的,我们可以不记录它的啊!
但是即使这样,状态还是有2^50,大神说了,要剪枝,于是我就剪了。
用优先队列。按金钱递减排序,以及花费时间递增排序。我们发现有些状态是没有用的,比如说(5,5)即花费时间5,得到金钱5,以及状态(5,4)花费时间5,得到金钱4,后者明显不如前者,则后者必然可以舍弃。也就是如果金钱少但是时间多,这样的状态可以舍弃。
两个优先队列,其实本质上是滚动数组,为什么要用优先队列呢?其实我觉得放进去之后排个序剪枝就好啦,(用优先队列总给我一种莫名的不安全感),但是东西不能用数组存,因为你不知道大小,只能用容器,那就优先队列吧,反正时间差不多。
剪枝剪得不好会MLE的,嗯,打错也会MLE。
因为联通块的才能一起选,所以就分成几部分做,用并查集求联通块。
1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 #include<algorithm> 6 #include<queue> 7 #include<cmath> 8 using namespace std; 9 #define Maxn 60 10 #define INF 0xfffffff 11 12 int t[Maxn],g[Maxn],k[Maxn][Maxn]; 13 int fa[Maxn]; 14 15 struct node 16 { 17 int x,y;//time money 18 friend bool operator < (node x,node y) 19 { 20 if(x.y==y.y) return x.x<y.x; 21 return x.y<y.y; 22 } 23 }; 24 25 priority_queue<node> q[2]; 26 27 int mymin(int x,int y) {return x<y?x:y;} 28 29 int ffind(int x) 30 { 31 if(fa[x]!=x) fa[x]=ffind(fa[x]); 32 return fa[x]; 33 } 34 35 int main() 36 { 37 int T,kase=0; 38 scanf("%d",&T); 39 while(T--) 40 { 41 int n,m; 42 scanf("%d%d",&n,&m); 43 for(int i=1;i<=n;i++) fa[i]=i; 44 for(int i=1;i<=n;i++) 45 { 46 int k; 47 scanf("%d%d%d",&t[i],&g[i],&k); 48 while(k--) 49 { 50 int x; 51 scanf("%d",&x); 52 fa[ffind(x)]=ffind(i); 53 } 54 } 55 56 int ans=INF; 57 58 node ft; 59 ft.x=0;ft.y=0; 60 q[0].push(ft); 61 for(int l=1;l<=n;l++) if(fa[l]==l) 62 { 63 while(!q[0].empty()) q[0].pop(); 64 while(!q[1].empty()) q[1].pop(); 65 q[0].push(ft); 66 for(int i=1;i<=n;i++) if(ffind(i)==l) 67 { 68 while(!q[0].empty()) 69 { 70 node x=q[0].top(); 71 q[0].pop(); 72 q[1].push(x); 73 node now; 74 now.y=x.y+g[i]; 75 now.x=x.x+t[i]; 76 77 if(now.y>=m) 78 { 79 ans=mymin(ans,now.x); 80 continue; 81 } 82 if(now.x>=ans) continue; 83 84 q[1].push(now); 85 } 86 // 优化减枝 87 int minn=INF; 88 while(!q[1].empty()) 89 { 90 node x=q[1].top();q[1].pop(); 91 if(x.x<minn) q[0].push(x),minn=x.x; 92 if(x.y==m) ans=mymin(ans,x.x); 93 } 94 } 95 } 96 printf("Case %d: ",++kase); 97 if(ans==INF) printf("Poor Magina, you can't save the world all the time!\n"); 98 else printf("%d\n",ans); 99 } 100 return 0; 101 }
2016-10-14 20:53:14