1. 程式人生 > 其它 >帶依賴條件的揹包問題

帶依賴條件的揹包問題

技術標籤:演算法

題目連結: https://www.nowcoder.com/ta/huawei
題 HJ16

思路:要轉化為揹包問題,將附件歸類到主件中,求該主件最優解時,不止考慮拿主件,還要考慮 主件+附件1 主件+附件2 主件+附件1+附件2 共4中情況,取最優

package javainterviews.huawei;

import java.util.ArrayList;
import java.util.Scanner;

/**
 * https://www.nowcoder.com/ta/huawei
 * HJ16: 帶依賴的揹包問題
 *
 * 王強今天很開心,公司發給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
 *
 * 
 * @author cantfu
 * @date 2021/1/17
 */
public class HJ16 { public static void main(String[] args) { int times = 10; //價格縮小倍數,用來減少計算量 Scanner sc = new Scanner(System.in); int N = sc.nextInt(); int m = sc.nextInt(); N = N/times; //若價格都是100整數,則減少計算量 // ArrayList<Good> goods = new ArrayList<>(m);
Good[] all = new Good[m+1]; for (int i = 1; i <= m; i++) { int p = sc.nextInt(); //price int w = sc.nextInt(); //weight int c = sc.nextInt(); //category Good good = new Good(p/times, w); if (c == 0) { all[i] = good;
} else { //將附件放入主件中 if (all[c].f1 == null) { all[c].f1 = good; } else { all[c].f2 = good; } } } int[][] dp = new int[m+1][N+1]; // DP for (int i = 1; i <= m; i++) { // 這裡其實不需要m, 只需要主件個數,下面執行到附件時會直接拿上一個的值. if (all[i] == null) { // 跳過all[i]為附件的情況 for (int j = 1; j <= N; j++) { dp[i][j] = dp[i - 1][j]; } continue; } // 附件價格 int f1 = all[i].f1 != null? all[i].f1.p : 0; int f2 = all[i].f2 != null? all[i].f2.p : 0; // 各情況價格和w*p int p1 = all[i].p; int v1 = all[i].w * all[i].p; int p2 = all[i].p + f1; int v2 = v1 + (all[i].f1 != null ? all[i].f1.w * all[i].f1.p : 0); int p3 = all[i].p + f2; int v3 = v1 + (all[i].f2 != null ? all[i].f2.w * all[i].f2.p : 0); int p4 = all[i].p + f1 + f2; int v4 = v1 + (all[i].f1 != null ? all[i].f1.w * all[i].f1.p : 0) + (all[i].f2 != null ? all[i].f2.w * all[i].f2.p : 0); for (int j = 1; j <= N; j++) { // // 4種情況取最優值, w*p dp[i][j] = dp[i-1][j]; // 1.只拿主件,且能放下, if (p1 <= j && p1 != 0) dp[i][j] = Math.max(dp[i][j], v1 + dp[i-1][j-p1]); // 這裡是用 dp[i][j]代表四種情況的當前最優 if(p2 <= j && f1 != 0) dp[i][j] = Math.max(dp[i][j], v2 + dp[i-1][j-p2]); if(p3 <= j && f2 != 0) dp[i][j] = Math.max(dp[i][j], v3 + dp[i-1][j-p3]); if(p4 <= j && f1 != 0 && f2 != 0) dp[i][j] = Math.max(dp[i][j], v4 + dp[i-1][j-p4]); } } System.out.println(dp[m][N]*times); } static class Good { int p; int w; Good f1; Good f2; Good(int p, int w) { this.p = p; this.w = w; } } }