题干: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