java打怪升级代码_1255: 打怪升级(Java)
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
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。
總結
以上是生活随笔為你收集整理的java打怪升级代码_1255: 打怪升级(Java)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用 .NET HttpClient
- 下一篇: 如何写一个简单的Web Service