1255: 打怪升级(Java)
WUSTOJ 1255: 打怪升级
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
题目分析
读完这个题目,我们首先应该想到的是,这两瓶药水应该怎么用。
我们都知道:当力量越大的时候,“乘2药”的作用就越大,比如:力量为1,用了“乘2药”后,力量增加到了2;力量为2,用了“乘2药”,力量就增加到了4。因此,我们需要用搜索遍历
的方法来计算最终用时,按照“乘2药”使用的瓶数来遍历。因此我们要讨论的问题是:战斗结束,你是否使用“乘2药”,用的话,你会用几瓶。
大致需要注意哪些:首先我们要知道搜索什么时候结束;力量值不能战斗胜利怎么办,力量值已经超过了此次战斗最大的力量值我们怎么处理。这3个问题弄明白之后,基本上就能解决了。
“加1药”只要有就用掉,它越早用越好,没必要留到后面用。
以上就是题目的粗略分析,不是很清楚的话,可以看代码,自认为注释非常详细了。
代码
// 1262ms
import java.util.Scanner;
public class Main {
// 关卡类,记录每场战斗的信息
private class Combat {
// 前面6个是输入的数据,即战斗的信息
// gapP = p2 - p1 后面计算用的比较频繁,直接算出来比较好
// gapT = t1- t2 同上
int p1, p2, t1, t2, w1, w2, gapP, gapT;
}
private Combat[] combat; // 记录所有战斗的信息,下标从0开始
private Scanner sc;
private int n, p; // 战斗场数,初始战力值
private double totalTime; // 所有战斗胜利用时,即最终结果
// battle()方法中要用到
private int newPower; // 新力量值
private int newBottles; // 新药水数量
private double newUsedTime; // 新用时
// 当前力量值与最低力量值的差,同最大力量差的比值,用于计算当前战斗用时
private double scale;
public Main() {
sc = new Scanner(System.in);
int i;
// 申请堆空间
combat = new Combat[1001];
for(i = 0; i < 1001; i++) {
combat[i] = new Combat();
}
while(input()) {
totalTime = Double.MAX_VALUE; // 赋最大值
battle(0, p, 0, 0); // 初始胜利0场,力量值p,药水0瓶,用时0秒
if(totalTime == Double.MAX_VALUE) {
System.out.println("Impossible");
} else {
System.out.printf("%.2f", totalTime);
System.out.println();
}
}
sc.close();
}
/**
* @return 是否结束输入
*/
private boolean input() {
n = sc.nextInt();
p = sc.nextInt();
if(0 == n) {
return false;
}
int i;
for(i = 0; i < n; i++) {
combat[i].p1 = sc.nextInt();
combat[i].p2 = sc.nextInt();
combat[i].t1 = sc.nextInt();
combat[i].t2 = sc.nextInt();
combat[i].w1 = sc.nextInt();
combat[i].w2 = sc.nextInt();
// 计算差值
combat[i].gapP = combat[i].p2 - combat[i].p1;
combat[i].gapT = combat[i].t1 - combat[i].t2;
}
return true;
}
/**
* @param wons 已经胜利战斗数量
* @param power 上一次战斗结束时候的力量值
* @param bottles 目前“乘2药”的瓶数
* @param usedTime 胜利wons场战斗用的时间
*/
private void battle(int wons, int power, int bottles, double usedTime) {
// 如果已经用的时间超过了目前的最短时间
// 后面的战斗也就没必要了,直接退出
if(usedTime > totalTime) {
return;
}
// 所有战斗都胜利了
if(wons == n) {
// 如果用时比目前最短时间还少的话
if(usedTime < totalTime) {
totalTime = usedTime; // 更新最短时间记录
}
return; // 退出
}
int i;
// 这是最后一场战斗,战斗前将药水用完,不用就浪费了
if(wons == n - 1) {
for(i = 0; i < bottles; i++) {
power *= 2;
}
bottles = 0;
}
// 循环考虑怎么处理剩余的药水
for(i = 0; i <= bottles; i++) {
if(i > 0) {
// i等于0的时候,表示不用药水,不能乘2;其他情况,每次用1瓶
power *= 2;
}
// 力量值不低于当前战斗要求的最低力量值,才能继续战斗;否则循环,继续用药水
if(power >= combat[wons].p1) {
// 力量值不低于要求最高力量值
if(power >= combat[wons].p2) {
// 力量值已经超过了最大值100
if(power >= 100) {
// 后面的所有战斗,将会以最快速度结束
for(i = wons; i < n; i++){
usedTime += combat[i].t2;
}
battle(n, power, 0, usedTime);
break;
}
// 当前战斗胜利,将“加1药”全部用完
newPower = power + combat[wons].w1;
// 原来有bottles瓶,用了i瓶,当前战斗胜利,获得了w2瓶
newBottles = bottles - i + combat[wons].w2;
newUsedTime = usedTime + combat[wons].t2;
battle(wons + 1, newPower, newBottles, newUsedTime);
break; // 这个break很关键,没有的话会超时,亲测...
} else { // 力量值低于最高力量值
newPower = power + combat[wons].w1;
newBottles = bottles - i + combat[wons].w2;
// 下面3行,计算当前战斗用时
scale = (power - combat[wons].p1) * 1.0 / combat[wons].gapP;
newUsedTime = usedTime;
newUsedTime += combat[wons].t1 - scale * combat[wons].gapT;
battle(wons + 1, newPower, newBottles, newUsedTime);
}
}
}
}
public static void main(String[] args) {
new Main();
}
}
代码补充
可能大家在Java里面数组内存
比较喜欢——用多少,就申请多少这种做法(反正我是这样的),但是这个题目,由于搜索开销较大,我们应该从开销方面来考虑如何写代码。题目指明可能有1000场战斗,也就是数组要1000大小,这么大的申请堆空间的时间开销还是很大的。我提交的相差了大约500ms,很可怕。所以此题应该直接一次性申请堆空间1001大小。
代码中有几个地方用到了break
,这里主要是为了除去不必要的遍历,以节约时间。这种做法也就是我们通常说的剪枝
,自认为剪的还不够好。如果大家通过阅读代码之后想到了更好的方法(也就是提交时间更短),或者还有疑问的话,欢迎评论以及留言(邮箱在友情链接
),我会及时回复。
小结
这个题目并没有我们想象的难,主要是注意一些细节问题。这道题目还检查出了我的关于内部类知识的漏洞,申请类对象数组需要两次new
,否则知识申请了一个数组,并没有实例。有这个疑问的可参考我自己写的与此对应的博客给内部类对象数组属性赋值时报错:Exception in thread “main” java.lang.NullPointerException。
1255: 打怪升级(Java)的更多相关文章
- 从苦逼到牛逼,详解Linux运维工程师的打怪升级之路
做运维也快四年多了,就像游戏打怪升级,升级后知识体系和运维体系也相对变化挺大,学习了很多新的知识点. 运维工程师是从一个呆逼进化为苦逼再成长为牛逼的过程,前提在于你要能忍能干能拼,还要具有敏锐的嗅觉感 ...
- SDUT oj 3005 打怪升级(内存搜索)
当比赛一直纠缠骑2如何做一个非常大的数量,数组不开啊...后来他们发现自己很傻啊,该数不超过最大10什么,这个上限就是力量100什么.. .. 其它的就是记忆化搜索啊,还有就是加一点力量的瓶子当时就要 ...
- Flask连接数据库打怪升级之旅
一.前言 在初学 Flask 的时候,在数据库连接这部分也跟每个初学者一样.但是随着工作中项目接手的多了,代码写的多了,历练的多了也就有了自己的经验和技巧.在对这块儿代码不断的进行升级改造后,整理了在 ...
- 运维工程师打怪升级进阶之路 V2.0
在此之前,发布过两个版本: 运维工程师打怪升级之路 V1.0 版本发布 运维工程师打怪升级必经之路 V1.0.1 很多读者伙伴们反应总结的很系统.很全面,无论是0基础初学者,还是有基础的入门者,或者是 ...
- 20190528-JavaScriptの打怪升级旅行 { 语句 [ 赋值 ,数据 ] }
写在前面的乱七八糟:今天考了试,emmm很基础的题,还是Mrs房的面试题让人绝望啊┓( ´∀` )┏,补了很多知识,很综合的题,坑也很多,总的来说,查漏补缺,其实是啥都缺~ 今天打的小BOSS主要是数 ...
- Oracle打怪升级之路二【视图、序列、游标、索引、存储过程、触发器】
前言 在之前 <Oracle打怪升级之路一>中我们主要介绍了Oracle的基础和Oracle常用查询及函数,这篇文章作为补充,主要介绍Oracle的对象,视图.序列.同义词.索引等,以及P ...
- “奥特曼攻打小怪兽”java学习打怪升级第一步
---恢复内容开始--- 练习:回合制对战游戏:奥特曼和小怪兽进行PK,直到一方的血量为0时结束战斗,输出谁胜利了! 不难看出场景中有两个对象:”奥特曼“这一对象抽象为”Ao"类: ...
- 【五年】Java打怪升级之路
之前写过一篇帖子.就是关于工作经验分享的,近期非常多人私信我.所以博客这边再分享一次 这几年来,我最大的感想就是一句话:多看.多写.多想.多问.多分享.多优化.多运动... 1.[多看] 读万卷书,行 ...
- Java自动化测试框架-03 - TestNG之Test Group篇 - 我们一起组团打怪升级(详细教程)
简介 其实这篇文章的group宏哥在上一篇中就提到过,但是就是举例一笔带过的,因此今天专门有一篇文章来讲解Group的相关知识.希望大家茅塞顿开 ,有着更进一步认识和了解测试组. 一.Test Gro ...
随机推荐
- vue中点击复制粘贴功能 clipboard 移动端
页面是由 v-for 循环渲染出来,要给每一个结构里面的复制按钮加一个复制功能 npm install clipboard --save 安装,如果安装处问题,多安装几次,我自己也安装了好几次 ...
- indexOf的用法
A.indexOf(B)="-1"表示的是不存在 不等于-1就表示存在 http://www.w3school.com.cn/jsref/jsref_indexOf.asp 没有出 ...
- js小脚本解析后台数据
java代码 List<CodeTableBean> clfsList = StandardCodeTable.getCodeTable("clfs", "& ...
- 【Linux性能调优一】观大局:系统平均负载load average
要测试linux系统性能及调优,首先要从全局检查linux的平均负载 1.什么是平均负载 load average 系统平均负载,平均负载是指单位时间内,系统处于可运行状态和不可中断状态的平均进程数, ...
- Oracle plsql 触发器 查询/启用/停止
在PLSQL中查询某个表的触发器脚本 select * from user_triggers where table_name='xxx' oracle触发器的启用和停用 1.禁用 table_nam ...
- Choosing a fast unique identifier (UUID) for Lucene——有时间再看下
Most search applications using Apache Lucene assign a unique id, or primary key, to each indexed doc ...
- OpenResty之 limit.count 模块
原文: lua-resty-limit-traffic/lib/resty/limit/count.md 1. 示例 http { lua_shared_dict my_limit_count_sto ...
- windows2012域控
此次主要是使用服务器搭建域控制器,我主要是用于使用office web apps 和office online server搭建在线Word预览编辑! 一.准备工作 首先准备1台比较干净的服务器,推荐 ...
- c++ 套接字 --->2002 java NIO --->netty
c++ 套接字 --->2002 java NIO --->netty
- Ionic4.x Javascript 扩展 ActionSheet Alert Toast Loading 以及 ionic 手势相 关事件
1.ActionSheet 官方文档:https://ionicframework.com/docs/api/action-sheet <ion-header> <ion-toolb ...