题目描述
【攻城战】
一支攻城部队,有若干种大炮各座,以及数量有限的火药,每种大炮的威力不尽相同,且在每次开火之前都需要一定时间填充火药,
请你帮助指挥官在给定的时间结束之前或者火药存量耗尽之前给予城池最大的打击。
约束条件:
大炮每次开火的威力一样;
火药剩余量不小于大炮的消耗量,该大炮才能开火;
填充火药之外的时间忽略不计;
不同种大炮可以同时开火。
输入描述
第一行,整数N,M,T
N表示大炮种类个数
M表示火药总量
T表示攻城总时间
1<= N,M,T<= 1000
接下来N行,每一行三个整数A,B,C
A表示大炮的威力
B表示大炮每次攻击消耗的火药量
C表示大炮每次攻击填充火药的时间
0<= A,B,C<= 100000
输出描述
输出在给定的时间结束之前或者火药存量耗尽之前给予城池的最大的打击。
解题思路
这是一道背包问题的变体,可以使用动态规划求解。
定义状态:dp[i][j] 表示前i种大炮在火药剩余量不超过j的情况下,最大的威力是多少。
状态转移方程如下:
dp[i][j] = max(dp[i-1][j], max(dp[i][j-c[i][1]*k]+c[i][0]*k)),其中k表示第i种大炮在火药剩余量不超过j的情况下最多能攻击的次数,c[i][0]表示第i个大炮的威力,c[i][1]表示第i个大炮每次攻击需消耗的火药量。
时间复杂度为 O(NMT),空间复杂度为 O(NMT),其中N,M,T分别表示大炮种类个数,火药总量和攻城总时间。
参考代码
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int m = scanner.nextInt();
int t = scanner.nextInt();
int[][] c = new int[n + 1][3];
for (int i = 1; i <= n; i++) {
c[i][0] = scanner.nextInt(); // 威力
c[i][1] = scanner.nextInt(); // 消耗火药量
c[i][2] = scanner.nextInt(); // 填充时间
}
int[][] dp = new int[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= m; j++) {
int k = Math.min(j / c[i][1], t / c[i][2]); // 计算第i种大炮在火药剩余量不超过j的情况下最多能攻击的次数
for (int l = 0; l <= k; l++) {
dp[i][j] = Math.max(dp[i][j], dp[i-1][j-l*c[i][1]]+l*c[i][0]); // 状态转移方程
}
}
}
System.out.println(dp[n][m]);
}
}