思路:求解問題時,總是選當前最好的選擇,不從整體上考慮。因而選用貪心演算法必須保證當前選的最好的必定是整體最好的
。
示例
分發餅乾
假設你是一位很棒的家長,想要給你的孩子們一些小餅乾。但是,每個孩子最多隻能給一塊餅乾。對每個孩子 i ,都有一個胃口值 gi ,這是能讓孩子們滿足胃口的餅乾的最小尺寸;並且每塊餅乾 j ,都有一個尺寸 sj 。如果 sj >= gi ,我們可以將這個餅乾 j 分配給孩子 i ,這個孩子會得到滿足。你的目標是儘可能滿足越多數量的孩子,並輸出這個最大數值。
假設輸入[1,2], [1,2,3]
,那麼輸出為2
。分析如下
- 要儘可能的滿足更多的小孩,那麼最小尺寸的餅乾應該分給最小胃口的那個人,這樣才不至於後面胃口大的小孩吃不到,兒胃口大的小孩吃小的肯定無法滿足。這種選擇恰好也是全域性最佳的選擇
public int findContentChildren(int[] g, int[] s) {
Arrays.sort(g);
Arrays.sort(s);
int i=0;
int j=0;
int num=0;
while(i<g.length && j<s.length){
if(s[j]>=g[i]){
num++;
i++;
j++;
}else{
j++;
}
}
return num;
}
複製程式碼
任務排程器
給定一個用字元陣列表示的 CPU 需要執行的任務列表。其中包含使用大寫的 A - Z 字母表示的26 種不同種類的任務。任務可以以任意順序執行,並且每個任務都可以在 1 個單位時間內執行完。CPU 在任何一個單位時間內都可以執行一個任務,或者在待命狀態。
然而,兩個相同種類的任務之間必須有長度為 n 的冷卻時間,因此至少有連續 n 個單位時間內 CPU 在執行不同的任務,或者在待命狀態。
你需要計算完成所有任務所需要的最短時間。
假如輸入 tasks = ['A','A','A','B','B','B'], n = 2
輸出為8
,執行順序: A -> B -> (待命) -> A -> B -> (待命) -> A -> B
。分析如下
- 為了使得整體時間最短,那麼冷卻時間肯定是最少的,因此要儘可能保證兩個相同的任務之間的執行間隔為n。換句話說就是
貪心的選擇執行n個不一樣的任務
,使得CPU能夠充分利用 - 要選擇先執行的任務,得考慮如何使得當前選擇整體是最優的,加入隨便選擇一個任務A執行,當存在一個任務B它的任務數比選擇的任務數要多時,這意味著B之間少了一次執行A的機會,也就是增加了要冷卻的風險,因而每次選擇任務數最多的來執行
public int leastInterval(char[] tasks, int n) {
int[] taskArr=new int[26];
for(char c:tasks){
taskArr[c-'A']++;
}
int maxLength=n;
int interval=0;
while (havaTask(taskArr)){
//先執行任務數最多的任務
int top = getTopTaskNotExecute(taskArr,Collections.emptyList());
interval++;
List<Integer> list = new ArrayList<>();
list.add(top);
for (int i=0;i<maxLength;i++)
{
//貪心的選擇沒有執行過的‘n’個任務最多而且沒有執行過的任務
Integer nextTop = getTopTaskNotExecute(taskArr, list);
if (nextTop==-1){
maxLength=list.size()-1;
break;
}
interval++;
list.add(nextTop);
}
if (list.size()-1!=n && havaTask(taskArr)){
interval+=n-list.size()+1;
}
}
return interval;
}
public boolean havaTask(int[] tasks){
for(int i=0;i<tasks.length;i++){
if(tasks[i]>0){
return true;
}
}
return false;
}
public Integer getTopTaskNotExecute(int[] tasks,List<Integer> executeTasks){
int maxTaskNums=0;
int maxNumsTaskIndex=-1;
for(int i=0;i<tasks.length;i++){
if(!executeTasks.contains(i) && tasks[i]>maxTaskNums){
maxTaskNums=tasks[i];
maxNumsTaskIndex=i;
}
}
if(maxNumsTaskIndex!=-1){
tasks[maxNumsTaskIndex]--;
}
return maxNumsTaskIndex;
}
}
複製程式碼
當然如果只要解決問題,可以直接計算出結果
還是使用貪心的策略來作為邏輯考慮的
- 假設只有1個任務數最多,而且是k個,共需要至少間隔數為 k-1 個間隔,那麼間隔消耗時間為 n*(k-1),k個任務本身執行時間為 k,總共時間至少為 n*(k-1)+k
- 假設最多的任務數一樣的不止1個有m個,當執行完一遍最多工數一個時,還有剩餘的 m-1 個任務,而且他們的數量肯定都是1,且各不相同,所以總共時間為 n*(k-1)+k+(m-1)=(n+1)*(k-1)+m
- 上述假設只是從單個任務的最多量來看的,沒有考慮獨立的任務數,有多少個任務肯定至少要執行多少的時間,最終取最大值即可
public int leastInterval(char[] tasks, int n) {
int[] taskArr=new int[26];
for(char c:tasks){
taskArr[c-'A']++;
}
Arrays.sort(taskArr);
int m=0;
for(int i=25;i>-1;i--){
if(taskArr[i]==taskArr[25]){
m++;
}else{
break;
}
}
return Math.max(tasks.length,(taskArr[25]-1)*(n+1)+m);
}
複製程式碼