SZTUOJ 1009.打怪升级

Description

对于多数RPG游戏来说,除了剧情就是打怪升级。本题的任务是用最短的时间取得所有战斗的胜利。这些战斗必须按照特定的顺序进行,每打赢一场,都可能会获得一些补药,用来提升力量。本题只有两种补药:“加1药”和“乘2药”,分别让你的力量值加1和乘以2。

战斗时间取决于你的力量。每场战斗可以用6个参数描述:p1, p2, t1, t2, w1, w2。如果你的力量小于p1,你将输掉战斗;如果你的力量大于p2,需要t2秒赢得战斗;如果力量位于p1和p2(包括p1和p2),战斗时间从t1线性递减到t2。比如p1=50,p2=75,t1=40,t2=15,你的力量为55,则战斗获胜需要35秒。注意,战斗时间可能不是整数。最后两个参数w1和w2分别表示战斗胜利后获得的“加1药”和“乘2药”的数量。注意,你不一定要立刻使用这些补药,可以在需要的时候再用,但不能在战斗中使用补药。

按顺序给出每场战斗的参数,输出赢得所有战斗所需的最短总时间。战斗必须按顺序进行,且不能跳过任何一场战斗。

Input

输入最多包含25组测试数据。每组数据第一行为两个整数n和p(1<=n<=1000, 1<=p<=100),即战斗的场数和你的初始力量值。以下n行每行6个整数p1, p2, t1, t2, w1, w2(1<=p1<p2<=100, 1<=t2<t1<=100, 0<=w1,w2<=10),按顺序给出各场战斗的参数。输入结束标志为n=p=0。

Output

对于每组数据,输出最短总时间(单位:秒),保留两位小数。如果无解,输出“Impossible”(不含引号)。

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

Hint

Source

湖南省第七届大学生计算机程序设计竞赛

题目解析

题目看上去很复杂,但是只要注意题目几个细节,很容易发现,力量最大为100.所以翻倍药最多需要喝7次。那么我们可以设计出状态dp[i] [j] [k]代表为打到第i个怪时候,攻击力为j,翻倍药的个数为k的最小时间 ,其中i最大为1000,j最大100,k最大为7.总复杂度为1000 * 100 * 7 * 7(第二个7是因为还要更新每一步是否喝药,喝几个药)那么我们就不需要管中间的过程。遇到+1的药直接吃。翻倍药留着或者吃。因为打完怪才给药。所以我们先转移打完怪只吃+1药的状态。在尝试喝掉所有剩余的翻倍药。所有的过程交给状态自己来处理。按顺序枚举即可更新出所有的状态。全部跑完后找答案的最小值即可。一个需要注意的细节是,在计算每一步需要的时间时。

tmp=t1[i]-((j - p1[i])*1.0/(p2[i]-p1[i]))*(t1[i]-t2[i]+0.0);

需要把分子分母都先变成浮点数。这道题的总状态数不多,所以不dp直接dfs搜也一样能解决问题,其他细节注释在代码里了

#include<bits/stdc++.h>
using namespace std;
const int maxn=1005;
const int inf=0x3f3f3f3f;
int n,p,t1[maxn],t2[maxn],p1[maxn],p2[maxn],w1[maxn],w2[maxn];
double dp[maxn][105][15];//状态为打到第i个怪时候,攻击力为j,翻倍药的个数为k的最小时间 
int main()
{
	while(scanf("%d %d",&n,&p),n+p)
	{
		for(int i=1;i<= n;i++)
		scanf("%d%d%d%d%d%d",&p1[i],&p2[i],&t1[i],&t2[i],&w1[i],&w2[i]);
		for(int i=0;i<=n;i++)
		for(int j=0;j<=101;j++)
		for(int k=0;k<=8;k++)
		dp[i][j][k]=inf;
		dp[0][p][0]=0;
		for(int i=1;i<=n;i++)
			for(int j=0;j<=101;j++)
				for(int k=0;k<=8;k++)
				{
					if(dp[i-1][j][k]==inf)continue;//表示无法从上一步转移过来
					if(j<p1[i])continue;//打不过怪
					double tmp;
					if(j>p2[i])tmp=t2[i];
					else if(j<=p2[i]&&j>=p1[i])
					tmp=t1[i]-((j - p1[i])*1.0/(p2[i]-p1[i]))*(t1[i]-t2[i]+0.0);//计算应该加的时间
					int at1=j+w1[i];//+1药直接喝。 
					int at2=k+w2[i];//翻倍药先留着。后续在更新是否喝药的状态 
					if(at1>101)at1=101;
					if(at2>8)at2=8;
					dp[i][at1][at2]=min(dp[i][at1][at2],dp[i-1][j][k]+tmp);//打怪后的状态可以直接转移 
					int at3=at1;
					for(int _=1;_<=at2;_++)//维护喝药后的状态
					{
						 at3*=2;
						 if(at3>101)dp[i][101][at2-_]=min(dp[i][101][at2-_],dp[i][at1][at2]);
						 else dp[i][at3][at2-_]=min(dp[i][at3][at2-_],dp[i][at1][at2]); 
					}	
				}
		double ans=inf;
		for(int i=0;i<=101;i++)
		for(int j=0;j<=8;j++)
		ans=min(ans,dp[n][i][j]);
		if(ans==inf)printf("Impossible\n");
		else printf("%.2lf\n",ans);
	} 
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值