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");
    }
}





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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值