買糖果(京東2016實習生真題)
題目描述
某糖果公司專門生產兒童糖果,它最受兒童歡迎的糖果有A1、A2兩個序列,均採用盒式包裝。包裝好的A1類糖果體積爲一個存儲單位,而包裝好的A2類糖果體積正好是A1類的兩倍。
這兩類糖果之所以廣受兒童歡迎,是因爲糖果中含有公司獨家研發的魔幻因子。A1或A2序列中的糖果,看起來包裝可能是一樣的,但因爲其中的魔幻因子含量不同被細分爲不同的產品。
臨近傳統節日,公司的糖果供不應求。作爲一個精明的糖果分銷商,小東希望能夠藉此大賺一筆,於是帶着現金開着貨車來公司提貨。貨車的容量是確定的,小東希望採購的糖果能夠儘可能裝滿貨車,且糖果的魔幻因子總含量最高。只要不超出貨車容量,糖果總可以裝入貨車中。
小東希望你能幫她解決這一問題。
import java.util.Scanner;
/**
* 標準的0-1揹包問題,使用動態規劃解決!
*
*
*/
public class Main{
public static void main(String[] args){
Scanner input = new Scanner(System.in);
while(input.hasNextInt()){
// 1、從鍵盤獲取輸入,順便判斷容量是否能裝下所有糖果(節約內存!)
// 若不進行判斷提交時會有內存溢出問題
int n = input.nextInt();
int v = input.nextInt();
int[] nums = new int[n];
int[] weights = new int[n];
int temp_w = 0;
int temp_n = 0;
for(int i=0; i<n; ++i){
weights[i] = input.nextInt();
nums[i] = input.nextInt();
temp_w += weights[i];
temp_n += nums[i];
}
// 2、若總的體積和小於等於容量,則每個糖果都能裝
if(temp_w <= v){
System.out.println(temp_n);
for(int i=1; i<=n; i++)
System.out.print(i + " ");
}
// 3、否則使用動態規劃。
else {
// 4、定義dp數組,dp[i][j]表示前0-i個糖果,容量爲j時最大魔幻因子,初始化dp[0][0] = 0
int[][] dp = new int[n + 1][v + 1];
// 5、狀態轉移,到第i個糖果,在容量足夠時,可以考慮裝和不裝;
// 當裝時:dp[i][j] = dp[i-1][j-weight[i-1]]+nums[i-1];
// 不裝時:dp[i][j] = dp[i - 1][j];
for (int i = 1; i <= n; i++) {
for (int j = 0; j <= v; j++) {
if (j < weights[i - 1]) {
dp[i][j] = dp[i - 1][j];
} else {
dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - weights[i - 1]] + nums[i - 1]);
}
}
}
// 6、dp[n][v]即是最大魔幻因子
int max = dp[n][v];
System.out.println(max);
if (max == 0) {
System.out.println("No");
}
// 7、打印最大魔幻因子時所裝的相應的糖果;
// 思路:當dp[i][v] != dp[i-1][v]時,說明取了第i個糖果,既然取了第i個糖果,剩下的v=v-weights[i-1]
else {
boolean[] flag = new boolean[n];
for (int i = n; i > 0; i--) {
if (dp[i][v] == dp[i - 1][v])
flag[i - 1] = false;
else {
flag[i - 1] = true;
v -= weights[i - 1];
}
}
for (int i = 0; i < n; ++i) {
if (flag[i])
System.out.print(i + 1 + " ");
}
}
}
}
}
}