帶依賴條件的揹包問題
阿新 • • 發佈:2021-01-18
技術標籤:演算法
題目連結: 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;
}
}
}