題目描述
王強今天很開心,公司發給N元的年終獎。王強決定把年終獎用於購物,他把想買的物品分爲兩類:主件與附件,附件是從屬於某個主件的,下表就是一些主件與附件的例子:
主件 附件
電腦 打印機,掃描儀
書櫃 圖書
書桌 檯燈,文具
工作椅 無
如果要買歸類爲附件的物品,必須先買該附件所屬的主件。每個主件可以有 0 個、 1 個或 2 個附件。附件不再有從屬於自己的附件。王強想買的東西很多,爲了不超出預算,他把每件物品規定了一個重要度,分爲 5 等:用整數 1 ~ 5 表示,第 5 等最重要。他還從因特網上查到了每件物品的價格(都是 10 元的整數倍)。他希望在不超過 N 元(可以等於 N 元)的前提下,使每件物品的價格與重要度的乘積的總和最大。
設第 j 件物品的價格爲 v[j] ,重要度爲 w[j] ,共選中了 k 件物品,編號依次爲 j 1 , j 2 ,……, j k ,則所求的總和爲:
v[j 1 ]*w[j 1 ]+v[j 2 ]*w[j 2 ]+ … +v[j k ]*w[j k ] 。(其中 * 爲乘號)
請你幫助王強設計一個滿足要求的購物單。
輸入描述:
輸入的第 1 行,爲兩個正整數,用一個空格隔開:N m
(其中 N ( <32000 )表示總錢數, m ( <60 )爲希望購買物品的個數。)
從第 2 行到第 m+1 行,第 j 行給出了編號爲 j-1 的物品的基本數據,每行有 3 個非負整數 v p q
(其中 v 表示該物品的價格( v<10000 ), p 表示該物品的重要度( 1 ~ 5 ), q 表示該物品是主件還是附件。如果 q=0 ,表示該物品爲主件,如果 q>0 ,表示該物品爲附件, q 是所屬主件的編號)
輸出描述:
輸出文件只有一個正整數,爲不超過總錢數的物品的價格與重要度乘積的總和的最大值( <200000 )。
示例1
輸入
複製
1000 5
800 2 0
400 5 1
300 5 1
400 3 0
500 2 0
輸出
複製
2200
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
//獲取揹包容量
int m = sc.nextInt();
//獲取物品數量
int n = sc.nextInt();
int[] v = new int[n+1];
int[] p = new int[n+1];
int[] q = new int[n+1];
int groups = 0;
for(int i = 1; i<=n; i++){
v[i] = sc.nextInt();
p[i] = sc.nextInt();
q[i] = sc.nextInt();
if(q[i] == 0) {
//根據主件個數,決定分多少組。即揹包問題的物品數量
groups++;
}
}
//分組
//定義容量數組
int[][] _v = new int[groups +1][4];
//定義價值數組
int[][] _p = new int[groups +1][4];
//得到賦值後的數組
processData(q, v, p, _v, _p);
int gc = _v.length;
//gc爲揹包問題實際上的商品數量-1,這樣方便後面循環i< gc
//System.out.println("gc="+gc);
//m爲揹包問題實際上的容積上限,故意m+1,這樣取值可以包括m
int[][] r = new int[gc][m+1];
//聲明一個最大容量最大價值的數組
for(int i = 1; i< gc; i++){//商品數量
for(int j = 1; j<= m; j++){//商品體積
//揹包問題-縮短爲子問題-固定狀態-假設不放第i件商品,此時實際佔用容量爲j,價值最大
int max = r[i-1][j];//假設爲不裝i的價值最大
//遍歷看裝i個商品的時候,裏面附件和主件數量的綜合
for (int t = 1; t < _v[i].length; t++) {
//System.out.println("t="+t);
//取processData過程賦的值
int tempv = _v[i][t];//當揹包裝i組商品時,t=主件+附件數量,實際體積
int tempp = _p[i][t];//裝第i組商品時,實際價值
if(tempv != 0 && tempv <= j) {
int maxBak = r[i - 1][j - tempv] + tempp;//假設裝i的時候價值最大
max = Math.max(max, maxBak);//驗證假設,求實際的最大值
}
}
//此值就是在j容量下裝i組商品的真正最大價值
r[i][j] = max;
//System.out.print("r[i][j] = max:"+max);
}
}
//那麼本題實際有gc-1組商品,容量爲m.其最大值爲r[gc-1][m]
System.out.println(r[gc -1][m]);
}
//記得審題。如果 q>0 ,表示該物品爲附件, q 是所屬主件的編號
private static void processData(int[] m, int[] v, int[] p, int[][] _v, int[][] _p) {
Map<Integer, List<Integer>> groups = new HashMap<>();
for (int i = 1; i < m.length; i++) {
if(m[i] == 0 ) {
//第i件爲主件
if(!groups.containsKey(i)) {
List<Integer> temp = new ArrayList<Integer>();
//記錄此商品序號
temp.add(i);
//以商品序號-即主件編號當成key
groups.put(i, temp);
}
}else {
//System.out.println("aaaa 件商品的附件編號爲m[i]="+m[i] + ",第i件商品="+i);
//第i件q>0爲附件
if (groups.containsKey(m[i])) {
////記錄附件所屬的主件編號一樣的話
//取出value集合
List<Integer> list = groups.get(m[i]);
//增加此附件商品序號
list.add(i);
}else {
List<Integer> temp = new ArrayList<Integer>();
//記錄附件所屬的主件編號-也即自己主件的商品序號
temp.add(m[i]);
//記錄此附件商品序號
temp.add(i);
//記錄附件所屬的主件編號
groups.put(m[i], temp);
}
}
}
int index = 1;
//遍歷得出所有的大組,大組裏麪包括主件商品序號,有附件的還包括附件商品序號
for(List<Integer> list : groups.values()) {
int size = list.size();
if(size == 1) {
//說明只有主件,沒有附件。那麼給數組賦值
_v[index][1] = v[list.get(0)];//v[list.get(0)]主件的體積
_p[index][1] = p[list.get(0)] * v[list.get(0)];//list.get(0)] * v[list.get(0) 記得審題,本題中說每件物品的價格與重要度的乘積的總和最大
//那麼主件的價值就是 價格v[list.get(0)*重要度 p[list.get(0)
}else if (size == 2) {
//有一個主件一個附件,可以有兩種購買方案
//第一種,只買主件
_v[index][1] = v[list.get(0)];
_p[index][1] = p[list.get(0)] * v[list.get(0)];
//第二種,既買主件也買附件
_v[index][2] = v[list.get(0)] + v[list.get(1)];
_p[index][2] = p[list.get(0)] * v[list.get(0)] + p[list.get(1)] * v[list.get(1)];
}else {
//System.out.println("size="+size);
//有一個主件二個附件,可以有三種購買方案
//第一種,只買主件
_v[index][1] = v[list.get(0)];
_p[index][1] = p[list.get(0)]* v[list.get(0)];
//第二種,既買主件也買一個附件
//這裏要特別注意,搭配1:主件+附件一 搭配2:主件+附件2,所以要算兩次取最大值賦值
int vMax = v[list.get(0)] + v[list.get(1)];//主件+附件一
int vMaxBak = v[list.get(0)] + v[list.get(2)];//主件+附件二
_v[index][2] = Math.max(vMax,vMaxBak);//取最大搭配
int pMax = p[list.get(0)] * v[list.get(0)] + p[list.get(1)] * v[list.get(1)];//主件+附件一
int pMaxBak = p[list.get(0)] * v[list.get(0)] + p[list.get(2)] * v[list.get(2)];//主件+附件二
_p[index][2] = Math.max(pMax,pMaxBak);//取最大搭配
//第三種,既買主件也買二個附件
_v[index][3] = v[list.get(0)] + v[list.get(1)] + v[list.get(2)];
_p[index][3] = p[list.get(0)] * v[list.get(0)] + p[list.get(1)]* v[list.get(1)] + p[list.get(2)]* v[list.get(2)];
}
index++;
};
}
}