參考博文:https://blog.csdn.net/kiritow/article/details/52208488
題目描述
有N項工作,每項工作分別在Si時間開始,在Ti時間結束。對於每項工作,你都可以選擇參與與否。如果選擇了參與,那麼自始至終都必須全程參與。此外,參與工作的時間段不能重疊(即使是開始的瞬間和結束的瞬間重疊也是不允許的)。
目標是儘可能參與可能多的工作,那麼最多能參與多少項工作?
樣例輸入
n = 5, s= {1,2,4,6,8}, t={3,5,7,9,10}
樣例輸出
3 (選取工作1、3、5)
題目分析
作爲一個正常人,我們不太可能一眼就想到用貪心算法來解決這個問題,這裏的貪心用法是在多次優化後得出的。
首先想到的一般是得到一個解空間,通過暴力解法得到所有的可能,再逐一的去驗證,在符合的方案中找到一個最優的方案。
最優化問題都可以通過某種搜索獲得最優解,最多區間調度問題也不例外。該問題無非就是選擇幾個不重疊的區間而已,看看最多能選擇多少個,其解空間爲一棵二叉子集樹,某個區間選或者不選構成了兩個分支,如圖四所示。我們的目標就是遍歷這棵子集樹,然後看從根節點到葉節點的不重疊區間的最大個數爲多少。可以看出,該問題的解就是n位二進制的某個0/1組合。子集樹共有2^n種組合,每種組合都需要判斷是否存在重疊區間,如果不重疊則獲得1的個數。
上述的例子一共八種組合:
- a
- ab
- ac
- ad
- abc
- abd
- acd
- abcd
現在我們已經得到解空間了,在不進行如何優化的前提下暴力求解。此時如果abcd不是有序排列的區間,則每種組合判斷是否有重疊區間的複雜度爲O(n^2):{爲了確保所有的區間都不覆蓋,a應與bcd分別比較共3次,b再與cd分別比較共2次,c再與d比較共1次。數學表達即 (n-1)+(n-2)+(n-3)……+1=1/2(n^2-n)==>即每種組合檢驗是否有重疊區間的複雜度爲O(n^2)}
此時該算法的複雜度爲2^n*O(n^2),即O(2^n*n^2),是十分不理想的,現在進行第一次優化。
先將所有區間排個序(按開始時間或者結束時間)
這樣排完序後,判斷區間是否重疊的算法馬上就降到O(n)了。
然後讓我們進一步思考,我們能不能優化解空間呢?此處我們可以用貪心的解法,每一步都選擇最優的,這樣就可以把O(2^n)壓縮到O(1)。這個方法實在是巧妙,證明不會,就舉個反例吧!
如果按開始時間排:
這個策略顯然不行,結果只有a一個。
如果按結束時間排:
這個策略可以的,結果爲3個。想法爲儘可能先選最早結束的,這樣才能選到最多的任務。
證明:https://www.cnblogs.com/ordili/p/8495998.html
代碼
package POJ.動態規劃與貪心算法;
import java.util.Arrays;
public class Section {
public static void main(String[] args) {
int count=1;//最多可參與的工作數
Task[] t=new Task[] {new Task(1, 3),new Task(8,10),
new Task(4,7),new Task(6,9),new Task(2,5)};
Arrays.sort(t);
for(int i=0;i<t.length;++i) {
System.out.println(t[i]);
}
int end=0;
for(int i=1;i<t.length;++i) {
if(t[end].et<t[i].st) {
count+=1;
end=i;
}
}
System.out.println(count);
}
}
class Task implements Comparable<Task>{
int st;
int et;
public Task(int st, int et) {
super();
this.st = st;
this.et = et;
}
@Override
public int compareTo(Task o) {
return this.et-o.et;
}
@Override
public String toString() {
return st+","+et;
}
}