完全揹包問題
阿新 • • 發佈:2020-11-06
完全揹包問題貪心演算法入門
題目描述
一個揹包容量為c, 共有n中物品, 第i個物品的重量為Wi, 價值為Vi. 選擇物品裝入揹包, 可以只裝入一部分, 請問,如何裝,可以使揹包的價值最大.
解決思路
此題和0-1揹包問題的不同在於, 0-1揹包問題要求物品全部裝進去, 而完全揹包問題要求物品可以只裝入一部分,採用貪心的策略解決這個問題
核心策略就是 : 優先選擇單價高的物品裝入揹包
- 首先計算物品單價
- 優先選擇單價高的物品裝入揹包
- 如果物品重量小於揹包剩餘重量, 接著物品全部裝入揹包, 重新計算揹包剩餘重量, 裝入下一個物品
- 如果物品重量大於揹包剩餘價值, 物品部分裝入揹包, 此時揹包已經滿了, 不再裝入.
程式碼思路
// 因為要按照物品的單價排序, 所以可以將物品當作一個類
// 1.自定排序規則, java如何自定排序規則
// 2.java中使用Arrays.sort()或者Collections.sort()進行排序
/** * @Author Fizz Pu * @Date 2020/10/30 下午3:51 * @Version 1.0 * 失之毫釐,繆之千里! */ /** * 題目描述 * 臨近開學了,大家都忙著收拾行李準 備返校,但 I_Love_C 卻不為此擔心! 因為他的心思全在暑假作業上:目前為止還未開動。 * 暑假作業是很多張試卷,我們這些從試卷裡爬出來的人都知道,卷子上的題目有選擇題、填空題、簡答題、證明題等。 * 而做選擇題的好處就在於工作量很少,但又因為選擇題題目都普遍很長。如果有 5 張試卷,其中 4 張是選擇題,最後一張是填空題, * 很明顯做最後一張所花的時間要比前 4 張長很多。但如果你只做了選擇題,雖然工作量很少,但表面上看起來也已經做了4/5的作業了。 * I_Love_C決定就用這樣的方法來矇混過關,他統計出了做完每一張試卷所需的時間以及它做完後能得到的價值(按上面的原理,選擇題越多價值當然就越高咯)。 * 現在就請你幫他安排一下,用他僅剩的一點時間來做最有價值的作業。 * 輸入 * 測試資料包括多組。每組測試資料以兩個整數 M,N(1<M<20,1<N<10000) 開頭,分別表示試卷的數目和 I_Love_C 剩下的時間。 * 接下來有 M 行,每行包括兩個整數 T,V(1<T<N,1<V<10000)分別表示做完這張試卷所需的時間以及做完後能得到的價值,輸入以 0 0 結束。 * * 輸出 * 對應每組測試資料輸出 I_Love_C 能獲得的最大價值。保留小數點 2 位 * * 提示:float 的精度可能不夠,你應該使用 double 型別。 * * 樣例輸入 * 4 20 * 4 10 * 5 22 * 10 3 * 1 2 * 0 0 * 樣例輸出 * 37.00 */ import java.util.Arrays; import java.util.Scanner; /** * 此題可以轉換為完全揹包問題 * 不是0,1揹包問題,原因是作業可以只做一部分 * N : 可以看成揹包總重 * 試卷價值:物品價值, 試卷所需時間:物品重量 * 那個卷子單位時間的價值高,我就先做 */ public class Main { static class Node implements Comparable<Node>{ double price; int id; // 在陣列中的下標 public Node(double price, int id) { this.price = price; this.id = id; } @Override public int compareTo(Node ob){ if(ob.price > this.price) return 1; else return -1; } } double getMaxValue(double[] times, double[] value, int restTime) { int r = restTime; // 揹包剩餘重量 double sums = 0; // 要通過單價找到對應物品的時間,價值.單個數組排序的方式肯定不行 // 利用節點記錄下資訊即可 Node[] nodes = new Node[times.length]; for(int i = 0; i < times.length; ++i){ nodes[i] = new Node(value[i]/times[i], i); } // 降序排序 Arrays.sort(nodes); for(Node node: nodes){ int id = node.id; if(times[id] <= r){ sums += value[id]; r -= times[id]; } else { sums += (r * node.price); break; } } return sums; } public static void main(String[] args) { Main main = new Main(); // 讀取資料 Scanner scanner = new Scanner(System.in); int itemCount, restTime; while (true){ itemCount = scanner.nextInt(); restTime = scanner.nextInt(); if(itemCount == 0 && restTime == 0)break; double[] times = new double[itemCount]; double[] values = new double[itemCount]; for(int i = 0; i < itemCount; ++i){ times[i] = scanner.nextDouble(); values[i] = scanner.nextDouble(); } // java中使用System.out.printf()進行格式化輸出 System.out.printf("%.2f\n", main.getMaxValue(times, values, restTime)); } } }
思考:
其實可以有多種貪心策略, 比如此題中我可以先做價值大的作業, 也可以先做耗時最小的作業, 這些儘管是區域性最優解, 但不是全域性最優解. 貪心就是要選出一種策略, 它是區域性最優, 最後可以得到全域性最優 可以某些區域性最優不一定可以到達全域性最優. 想想做人有時候也是這樣啊, 步步為贏,每一步都順風順水, 最終也不一定會得到最好的結果, 年輕人還是多用困難歷練以下自己