RPG battles UVA - 12294 DP詳解

UVA - 12294 

In many typical RPG games, you battle with bad guys, creatures, monsters or ghosts etc. all the time.

After each battle, you may get magic potions that power you up, so you’ll get stronger and stronger,
and finally defeat the big boss. In this problem, we only consider two kinds of potion: stronger and
double power. If you drink a bottle of stronger potion, your power increases by 1; if you drink a bottle
of double power potion, your power doubles.
How long each battle lasts depends on how powerful you are. Each battle is described by six
parameters: p1, p2, t1, t2, w1, w2. That means, if your power is less than p1, you will be defeated; if
your power is greater than p2, you’ll need t2 seconds to defeat all the enemies. If your power is between
p1 and p2 (inclusive), the time needed for the battle decreases linearly from t1 to t2. For example, if
p1 = 50, p2 = 75, t1 = 40, t2 = 15, and your power is 55, then the battle lasts for 35 seconds. Note that
the time needed for battles may be non-integers. The last two parameters, w1 and w2, represent the
number of bottles of stronger potion and double power potion you got after winning the battle. Note
that you don’t have to drink these potions immediately. You can drink them later if that’ll decrease
the total time. You cannot drink potions during a battle, so the time needed for battles is not affected by the potions.
Given the list of battles, your task is to find a strategy to win all the battles as quickly as possible.
Note that you must enter the battles in order. You cannot redo or skip any battle.
Input
There will be at most 25 test cases. Each test begins with two integers n and p (1 ≤ n ≤ 1000,
1 ≤ p ≤ 100), the number of battles and your initial power. Each of the next n lines contains 6 integers
p1, p2, t1, t2, w1, w2 (1 ≤ p1 < p2 ≤ 100, 1 ≤ t2 < t1 ≤ 100, 0 ≤ w1, w2 ≤ 10). The input is terminated
by a test case with n = p = 0, you should not process it.
Output
For each test case, print the shortest time (in seconds) to win all the battles, to two digits after the
decimal point. If you cannot win all the battles, print ‘Impossible’ (without quotes).
Sample Input
1 55
50 75 40 15 10 0
2 55
50 75 40 15 10 0
50 75 40 15 10 0
3 1
1 2 2 1 0 5
1 2 2 1 1 0
1 100 100 1 0 0
1 7
4 15 35 23 0 0
1 1
2 3 2 1 0 0
0 0
Sample Output
35.00
60.00
41.00
31.73
Impossible

題意:輸入n和p(1≤n≤1000,1≤p≤100),戰鬥次數和你的初始力量(必須按順序進入戰鬥。不能重做或跳過任何戰鬥)    兩種藥水:力量+1藥水和力量*2藥水。
輸入p1,p2,t1,t2,w1,w2。如果你的力量低於P1,你將被擊敗;如果你的力量大於p2,你需要t2秒來擊敗所有的敵人。如果你的權力在之間p1和p2(包括),戰鬥所需的時間從t1到t2線性減少。例如,如果p1 = 50,p2 = 75,t1 = 40,t2 = 15,你的力量是55,那麼戰鬥持續35秒。注意戰鬥所需的時間可能不是整數。最後兩個參數w1和w2代表在戰鬥結束後獲得的瓶裝力量+1藥水和力量*2藥水瓶數。注意你不必馬上喝這些藥水。可以將藥水贊一會兒再喝,可能會有更大的收益。
輸出:最短時間(以秒爲單位)以贏得所有戰鬥,打印後爲兩個數字小數點。如果你不能贏得所有的戰鬥,打印'Impossible'(不含引號)。


思路:現如果有5瓶+1藥水,和一瓶*2藥水,準備打最後一場戰鬥,喝藥順序怎樣才能使得力量最大化,肯定是*2藥水最後喝,+1藥水都先喝。在想一個問題若有+1藥水,要不要留到下一場戰鬥再喝, 思考後會發現  若有+1藥水就喝(早喝早+1大笑),這樣纔是最優的。
*2藥水就不一定要先喝,比如說現在只有一瓶*2藥水,這場戰鬥打完可以獲得10瓶+1藥水,  0瓶*2藥水。現在的力量是夠打這場戰鬥的,那麼就留下來*2藥水,  a*2+10跟(a+10)*2是差了10點力量的,這些多增加的力量對後來的戰鬥可能會很關鍵。

現在是+1藥水有就喝,*2藥水最多留7瓶,因爲(1<<7) >100,每場戰鬥最多需要100點力量,所以很容易想到DP枚舉所有情況

dp[ i ][ j ][ k ]表示獲得第i場戰鬥的勝利時,當前力量爲j,剩下k瓶*2藥水所花的最小時間(這個k表示是打完這場留下的k瓶,還沒有加上第i場獲得的藥水)

double  dp[1005][105][8];

代碼:這道題自己想了2個多小時才寫出來

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <string>
#include <map>
#include <queue>
#include <math.h>
#include <algorithm>
#define mem(a,b) memset(a,b,sizeof a)
#define LL long long
const int inf=0x3f3f3f3f;
const LL mod=10000;
const int N=1005;
using namespace std;
int n,p,p1,p2,t1,t2,sum,c;
double dp[1005][105][8];
int w1[1005];
int w2[1005];
double minn(double x,double y)
{
    return x<y?x:y;
}
double K(double x,double y)
{
    if(x>=p2) return t2;
    return t1+(x-p1)*y;//總共多了x-p1點力量,每點可以減|y|時間,(y<0)
}
int main()
{
    while(~scanf("%d%d",&n,&p)&&(n+p))
    {
        mem(w1,0);
        mem(w2,0);
        mem(dp,0);//初始化爲0,
        dp[0][p][0]=1;//初始化用時爲1,最後將這一秒減去即可,爲了區分這個位置是否有值,因爲將double dp初始化爲inf只能一個一個賦值,還是初始化爲0方便。所以dp不能直接求min,要先判斷dp中是否有值,有值就比較大小,爲0就直接賦值
        for(int i=1; i<=n; i++)//準備第i場戰鬥
        {
            scanf("%d%d%d%d%d%d",&p1,&p2,&t1,&t2,&w1[i],&w2[i]);
            double y=(t1-t2)*1.0/(p1-p2); //y表示每多一點力量可以減少多少的時間,減少的時間是|y|,y本身是小於零的
            for(int j=p; j<=100; j++)//枚舉第i-1場戰鬥過後,力量爲j,剩餘*2藥水k瓶。
            {
                for(int k=0; k<=7; k++)
                    if(dp[i-1][j][k])
                    {
                        int e=minn(k+w2[i-1],7);//先將上一場戰鬥的*2藥水拿上。
                        int e1=minn(j+w1[i-1],100);//將上一場戰鬥的+1藥水喝掉。
                        for(int h=e; h>=0; h--,e1*=2)//開始枚舉第i場戰鬥過後留下的*2藥水數量h,e1爲當前力量
                            if(e1>=p1)//力量大於p1才能通過戰鬥
                            {
                                if(e1>=100)//力量大於等於100,那麼就不需要再喝*2藥水了,算完直接break;
                                {//力量大於100的都按100來算
                                    if(dp[i][100][h]) dp[i][100][h]=minn(dp[i][100][h],dp[i-1][j][k]+K(100*1.0,y));
                                    else dp[i][100][h]=dp[i-1][j][k]+K(100*1.0,y);
                                    break;
                                }
                                if(dp[i][e1][h]) dp[i][e1][h]=minn(dp[i][e1][h],dp[i-1][j][k]+K(e1*1.0,y));//這個式子不難看懂,上個狀態的時間+用時,更新下一個狀態的用時
                                else dp[i][e1][h]=dp[i-1][j][k]+K(e1*1.0,y);//dp爲0 直接賦值,不爲0則取最小值。
                            }
                    }
            }
        }
        double ans=1e9*1.0;
        for(int i=0; i<=100; i++)
            for(int j=0; j<=7; j++)
                if(dp[n][i][j])
                    ans=minn(dp[n][i][j],ans);
        if(ans<1e9*1.0) printf("%.2f\n",ans-1);//最開始dp[0][p][0]=1,減掉了那個1秒。
        else printf("Impossible\n");
    }
}





發佈了156 篇原創文章 · 獲贊 56 · 訪問量 5萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章