小蓝面前有 N N N 件物品, 其中第 i i i 件重量是 W i W_{i} Wi, 价值是 V i V_{i} Vi 。她还有一个背包, 最大承重是 M M M 。
小蓝想知道在背包称重范围内, 她最多能装总价值多少的物品?
特别值得一提的是, 小蓝可以使用一个魔法 (总共使用一次), 将一件物品 的重量增加 K K K, 同时价值翻倍。(当然小蓝也可以不使用魔法)
第一行包含 3 个整数 N 、 M N 、 M N、M 和 K K K 。
以下 N N N 行, 每行两个整数 W i W_i Wi和 V i V_i Vi。
一个整数代表答案。
3 10 3
5 10
4 9
3 8
26
1 ≤ N ≤ 2000 , 1 ≤ M , K ≤ 10000 , 0 ≤ W i , V i ≤ 10000 1 \leq N \leq 2000,1 \leq M, K \leq 10000,0 \leq W_{i}, V_{i} \leq 10000 1≤N≤2000,1≤M,K≤10000,0≤Wi,Vi≤10000
背包与魔法
首先,想解决此题的前置知识是需要掌握普通的 01 01 01背包问题,当然这题肯定不可能这么简单。题目相对于 01 01 01背包来说,唯一的区别仅仅在于小蓝可以使用 1 1 1次魔法。我们只需要多加一维状态记录是否使用了魔法即可,下面考虑 d p dp dp状态。
f [ i ] [ j ] [ 0 ] f[i][j][0] f[i][j][0]:表示只考虑前 i i i个物品,背包容量为 j j j,并且还没有使用魔法的情况下的最大价值。我们考虑对该状态进行转移,因为这一状态是未使用魔法的,所以转移方程同 01 01 01背包一样。
f [ i ] [ j ] [ 0 ] = { f [ i − 1 ] [ j ] [ 0 ] 当 j < w [ i ] m a x ( f [ i − 1 ] [ j ] [ 0 ] , f [ i − 1 ] [ j − w [ i ] ] [ 0 ] ) 当 j > = w [ i ] f[i][j][0] = \begin{cases} f[i-1][j][0] &\text{当 $j
f [ i ] [ j ] [ 1 ] f[i][j][1] f[i][j][1]:表示只考虑前 i i i个物品,背包容量为 j j j,并且已经使用了魔法 (注意并不一定是对第 i i i个物品使用) 的情况下的最大价值。考虑每个背包的选择情况的,我将其分为三种情况考虑。
因为问题本质还是属于 01 01 01背包问题,所以我们可以使用滚动数组进代码进行时间复杂度和空间复杂的优化,压缩一维状态,状态转移可以写成:
f [ i ] [ 1 ] = { m a x ( f [ j − w [ i ] ] [ 0 ] + v [ i ] , f [ j ] [ 0 ] ) m a x ( m a x ( f [ j ] [ 1 ] , f [ j − w [ i ] ] [ 1 ] + v [ i ] ) , f [ j − ( w [ i ] + k ) ] [ 0 ] + v [ i ] ∗ 2 ) f[i][1]= \begin{cases} max(f[j-w[i]][0]+v[i],f[j][0]) &\text{}\\ max(max(f[j][1],f[j-w[i]][1]+v[i]),f[j-(w[i]+k)][0]+v[i]* 2) &\text{}\\ \end{cases} f[i][1]={max(f[j−w[i]][0]+v[i],f[j][0])max(max(f[j][1],f[j−w[i]][1]+v[i]),f[j−(w[i]+k)][0]+v[i]∗2)
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Scanner;
public class Main {
static PrintWriter out=new PrintWriter(new OutputStreamWriter(System.out));
static int N=2010,M=10010;
static int[] w=new int[N];
static int[] v=new int[N];
static long[][] f=new long[M][2];
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int k = sc.nextInt();
for (int i = 1; i <= n; i++) {
w[i] = sc.nextInt();
v[i] = sc.nextInt();
}
for (int i = 1; i <= n; i++) {
//j是容积
for (int j = m; j >=w[i]; j--) {
//如果能选且还没有使用魔法
f[j][0]=Math.max(f[j-w[i]][0]+v[i],f[j][0]);
//已经使用过魔法了
if (w[i]+k<=j){
f[j][1]=Math.max(Math.max(f[j][1],f[j-w[i]][1]+v[i]),f[j-(w[i]+k)][0]+v[i]* 2L);
}
}
}
out.println(Math.max(f[m][1],f[m][0]));
out.flush();
}
}