購物單問題-帶詳細註解

題目描述
王強今天很開心,公司發給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++;
        };
    }
    
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章