解题思想 - 动态规划
- 首先问题可以转换为:由q个不健身的日期(休息日)将n天划分为q+1个区间,求每个区间的最大健身奖励.最终所有区间的最大奖励和就是n天可获取的最大奖励.这个等价转换是一定正确的,是从题意获取的.
- 所以要求出每个区间的最大奖励,联想到"凑零钱"问题. 决定采用dp做法
- 如果每个区间都求一遍dp数组比较麻烦,不如直接一步求出dp数组.之后看区间多大就找dp数组中对应的结果即可
代码实现
import java.util.*;
public class LanQiao {
public static long[] dp(int days, List<int[]> choices) {
long[] dp = new long[days + 1];
dp[0] = 0;
for (int i = 1; i <= days; i++) {
for (int j = 0; j < choices.size(); j++) {
int taskDay = choices.get(j)[0];
int taskBonus = choices.get(j)[1];
if (taskDay <= i) {
dp[i] = Math.max(dp[i - taskDay] + taskBonus, dp[i]);
} else {
break;
}
}
}
return dp;
}
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int q = sc.nextInt();
ArrayList<Integer> points = new ArrayList<>(q + 2);
points.add(0);
for (int i = 1; i <= q; i++) {
points.add(sc.nextInt());
}
points.sort(new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1 - o2;
}
});
points.add(n + 1);
int[] intervals = new int[q + 1];
for (int i = 0; i < q + 1; i++) {
intervals[i] = points.get(i + 1) - points.get(i) -1;
}
List<int[]> tasks = new ArrayList<>(m);
for (int i = 0; i < m; i++) {
tasks.add(new int[]{(int)(Math.pow(2, sc.nextInt())), sc.nextInt()});
}
tasks.sort(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
return o1[0] - o2[0];
}
});
long[] dp = dp(n, tasks);
long sum = 0;
for (int i = 0; i < q + 1; i++) {
sum += dp[intervals[i]];
}
System.out.println(sum);
}
代码细节
- 求q+1个区间使用了小技巧, 阅读代码并结合下图体会:
- 测试数据说明给得太少, 比如q个休息日给的顺序是升序的,所以可以不用对points排序就可以直接当作升序来用; m个任务不是有序的, 必须排序后才能使用break剪枝,否则结果错误.
- 注意数据范围
- 查看其它提交结果,发现一个dp写法不一样的, 我也没见过该写法, 先记录下来未来留意
static long dp(int dayDis, int w[], int v[]) {
if (dayDis == 0)
return 0;
long dp[] = new long[dayDis + 1];
for (int y = 0; y < w.length; ++y) {
for (int i = w[y]; i <= dayDis; ++i) {
dp[i] = Math.max(dp[i], dp[i - w[y]] + v[y]);
}
}
return dp[dayDis];
}
题目
链接
蓝桥杯
问题描述
输入格式
输出格式
样例输入
10 3 3
1 4 9
0 3
1 7
2 20
样例输出
30