題幹:https://leetcode.com/problems/largest-values-from-labels/
本體可以看成是揹包問題的進階版,目標類似,是讓value最大化,不過有兩個約束條件,一是選取綜述不超過num_wanted, 二是選出的子集如果用label分組的話,每組的元素數量不超過use_limit。
思路大概如下:先把value值分組,並對每組內元素從大到小排序,然後組間按照第一個元素排序(從大到小)。
排序號每次摘取第一個組的第一個元素,即爲整體最大元素,這樣摘取num_wanted次,或者到無元素可摘的情況,每次摘取還要注意每組摘取的數量不得超過use_limit個,爲了更方便地實現這一點,可以在分組時就實現吧每組的元素限制在use_limit個,超過的就直接丟棄,因爲反正也選不上。
這裏的方法其實屬於貪心法,其實嚴格來說,這裏需要證明的爲什麼本處貪心法是work的,是能達到全局最優的,因爲這裏是個簡答求最大值問題,所以證明不會特別複雜,我這裏略過了。
代碼如下:
package com.example.demo.leetcode;
import java.util.*;
public class LargestValuesFromLabels {
public int largestValsFromLabels(int[] values, int[] labels, int num_wanted, int use_limit) {
Map<Integer, List<Integer>> label2values = new HashMap<>();
for(int i=0;i<labels.length;i++){
add2map(label2values, labels[i], values[i], use_limit);
}
List<List<Integer>> sortedList = new ArrayList<>();
for(Map.Entry<Integer, List<Integer>> entry : label2values.entrySet()){
//就是爲了收集排好序的分組,按照每組第一個元素的大小,從大到小排列
sortedList.add(entry.getValue());
}
Comparator<List<Integer>> comp = new Comparator<List<Integer>>(){
@Override
public int compare(List<Integer> s1, List<Integer> s2) {
if(s2.get(0)>s1.get(0)){
return 1;
}else if(s2.get(0).equals(s1.get(0))){
return 0;
}else if(s2.get(0)<s1.get(0)){
return -1;
}
return -1000;
}
};
Collections.sort(sortedList, comp);
int sum = 0;
for(int i=0;i<num_wanted;i++){
if(sortedList.size()<1){
break;
}
List<Integer> k = sortedList.get(0);
if(k.size()>0){
sum+=k.get(0);
sortedList.remove(0);
k.remove(0);
if(k.size()>0){
sortedList.add(k);
}
Collections.sort(sortedList, comp);
}
}
return sum;
}
private void add2map(Map<Integer, List<Integer>> mp, Integer key, Integer val, int longest){
if(mp.get(key)!=null){
List<Integer> list = mp.get(key);
int i=0;
while(i<list.size() && list.get(i)>val){
i++;
}
list.add(i, val);
if(list.size()>longest){
list.remove(list.size()-1);
}
}else{
List<Integer> eIndexs = new ArrayList<>();
eIndexs.add(val);
mp.put(key, eIndexs);
}
}
public static void main(String[] args) {
LargestValuesFromLabels demo = new LargestValuesFromLabels();
// int[] values = {5,4,3,2,1};
// int[] values = {9,8,8,7,6};
int[] values = {3,2,3,2,1};
// int[] values = {5963,15066,7870,4883,18205,19694,1938,2291,13915,16317,12347,19514,2766,13578,18721,892,1683,12691,6700,17793,19514,10687,8106,11006,11841,5992,602,7538,13220,15178,14142,7813,17632,16438,3652,9630,19297,19328,12118,18871,1024,10888,2668,6695,6990,8680,13515,10150,6218,18402};
// int[] labels = {0,0,0,1,1};
int[] labels = {1,0,2,2,1};
// int[] labels = {1,1,2,2,3};
// int[] labels = {7149,19554,17254,15626,5710,9132,9068,1432,14982,3596,12403,1746,10231,11836,449,15140,17696,17094,795,4355,1227,5973,18464,6046,10232,5538,10066,14692,18832,11861,7265,13952,9448,6164,14014,8073,13446,18649,18769,6285,13127,2109,13319,10794,9828,15545,12451,11604,4072,9114};
// int num_wanted = 3;
int num_wanted = 2;
int use_limit = 1;
int ret = demo.largestValsFromLabels(values, labels, num_wanted, use_limit);
System.out.println(ret);
}
}
反思: 一開始沒有想到用貪心法,導致嘗試用動態規劃做了很久,到頭來卻是OJ不停地報timeout,心力憔悴,還是不能一招動態規劃打天下啊。。。理論聯繫實際一下,扒動態規劃和貪心的關係: https://labuladong.gitbook.io/algo/dong-tai-gui-hua-xi-lie/tan-xin-suan-fa-zhi-qu-jian-tiao-du-wen-ti