动态规划-HJ16 购物单
描述
王强决定把年终奖用于购物,他把想买的物品分为两类:主件与附件,附件是从属于某个主件的,下表就是一些主件与附件的例子:
主件 | 附件 |
电脑 | 打印机,扫描仪 |
书柜 | 图书 |
书桌 | 台灯,文具 |
工作椅 | 无 |
如果要买归类为附件的物品,必须先买该附件所属的主件,且每件物品只能购买一次。
每个主件可以有 0 个、 1 个或 2 个附件。附件不再有从属于自己的附件。
王强查到了每件物品的价格(都是 10 元的整数倍),而他只有 N 元的预算。除此之外,他给每件物品规定了一个重要度,用整数 1 ~ 5 表示。他希望在花费不超过 N 元的前提下,使自己的满意度达到最大。
满意度是指所购买的每件物品的价格与重要度的乘积的总和,请你帮助王强计算可获得的最大的满意度。
输入描述:
输入的第 1 行,为两个正整数N,m,用一个空格隔开:
(其中 N ( N<32000 )表示总钱数, m (m <60 )为可购买的物品的个数。)
从第 2 行到第 m+1 行,第 j 行给出了编号为 j-1 的物品的基本数据,每行有 3 个非负整数 v p q
(其中 v 表示该物品的价格( v<10000 ), p 表示该物品的重要度( 1 ~ 5 ), q 表示该物品是主件还是附件。如果 q=0 ,表示该物品为主件,如果 q>0 ,表示该物品为附件, q 是所属主件的编号)
输出描述:
输出一个正整数,为张强可以获得的最大的满意度。
示例1
输入:
1000 5 800 2 0 400 5 1 300 5 1 400 3 0 500 2 0
输出:
2200
示例2
输入:
50 5 20 3 5 20 3 5 10 3 0 10 2 0 10 1 0
输出:
130
说明:
由第1行可知总钱数N为50以及希望购买的物品个数m为5; 第2和第3行的q为5,说明它们都是编号为5的物品的附件; 第4~6行的q都为0,说明它们都是主件,它们的编号依次为3~5; 所以物品的价格与重要度乘积的总和的最大值为10*1+20*3+20*3=130
解法:
具体思路就是构造物品类,然后对主件判断是否有附件,若有附件则依次添加,根据主件、附件1、附件2的组合有四种情况
- 只有主件
- 主件+附件1
- 主件+附件2
- 主件+附件1+附件2
根据以上情况转化问题为经典的 01背包问题 ,接着就是套公式动态规划即可
package dayone.easy; import java.util.Scanner; class Goods { int v; int p; boolean main = false; int a1 = -1; //定义附件1的编号 int a2 = -1; //定义附件2的编号 } /*** * 1000 5 * 800 2 0 * 400 5 1 * 300 5 1 * 400 3 0 * 500 2 0 */ class MtTest { public static int dw = 100;//加快运行 public static void main(String[] args) { boolean flag = true; Scanner sc = new Scanner(System.in); int m = sc.nextInt(); //总钱数 int n = sc.nextInt(); //购买个数 //初始化goods Goods[] goods = new Goods[m]; for (int i = 1; i <= n; i++) { goods[i] = new Goods(); } //填充goods,并将附件编号加在对应的主件上(赋值到a1,a2) for (int i = 1; i <= n; i++) { int v = sc.nextInt(); int p = sc.nextInt(); int q = sc.nextInt(); if (flag) { if (v % dw != 0) { flag = false; dw = 10; for (int j = 1; j < i; j++) goods[j].v = goods[j].v * 10; } } v = v / dw; goods[i].p = p; goods[i].v = v; if (q == 0) { goods[i].main = true; } else if (goods[q].a1 == -1) { goods[q].a1 = i; } else { goods[q].a2 = i; } } m/=dw; //利用动态规划01背包问题解决该问题 int[][] dp = new int[n + 1][m + 1]; //物品 for (int i = 1; i <= n; i++) { boolean main = goods[i].main; int v1 = 0, v2 = 0, v3 = 0, tempDp = 0, tempDp1 = 0, tempDp2 = 0, tempDp3 = 0; int v = goods[i].v; int p = goods[i].p; int a1 = goods[i].a1; int a2 = goods[i].a2; //第一种情况:全部选择主件 tempDp = p * v; //第二种情况:选择主件的同时选择a1和a2作为附件 if (a1 != -1 && a2 != -1) { v1 = v + goods[a1].v + goods[a2].v; tempDp1 = tempDp + goods[a1].v * goods[a1].p + goods[a2].v * goods[a2].p; } //第三种情况:选择主件同时选择a2作为附件 if (a2 != -1) { v2 = v + goods[a2].v; tempDp2 = tempDp + goods[a2].v * goods[a2].p; } //第四种情况:选择主件同时选择a1作为附件 if (a1 != -1) { v3 = v + goods[a1].v; tempDp3 = tempDp + goods[a1].v * goods[a1].p; } //金额 for (int j = 1; j <= m; j++) { dp[i][j] = dp[i - 1][j]; if (!main) continue; if (v != 0 && j >= v) dp[i][j] = Math.max(dp[i][j], tempDp + dp[i - 1][j - v]); if (v1 != 0 && j >= v1) dp[i][j] = Math.max(dp[i][j], tempDp1 + dp[i - 1][j - v1]); if (v2 != 0 && j >= v2) dp[i][j] = Math.max(dp[i][j], tempDp2 + dp[i - 1][j - v2]); if (v3 != 0 && j >= v3) dp[i][j] = Math.max(dp[i][j], tempDp3 + dp[i - 1][j - v3]); } } System.out.println(dp[n][m]*dw); } }
我只想安静地学习,捡拾前人的牙慧,默默强大如此弱小的我...