算法思想:貪心算法
實際問題:活動安排問題
編寫語言:Java
問題描述
設有n個活動的集合 E = {1,2,…,n},其中每個活動都要求使用同一資源,如演講會場等,而在同一時間內只有一個活動能使用這一資源。每個活動 i 都有一個要求使用該資源的起始時間 si 和一個結束時間 fi,且 si < fi。如果選擇了活動 i,則它在半開時間區間 [si, fi) 內占用資源。若區間 [si, fi) 與區間 [sj, fj) 不相交,則稱活動 i 與活動 j 是相容的。也就是說,當 si ≥ fj 或 sj ≥ fi 時,活動 i 與活動 j 相容。活動安排問題就是要在所給的活動集合中選出最大的相容活動子集合。
將活動按照結束時間進行從小到大排序。然后用 i 代表第 i 個活動,s[i] 代表第 i 個活動開始時間,f[i] 代表第 i 個活動的結束時間。挑選出結束時間盡量早的活動(活動結束時間最早的活動),並且滿足后一個活動的起始時間晚於前一個活動的結束時間,全部找出這些活動就是最大的相容活動子集合。即有活動 i,j 為 i 的下一個活動。f[i]最小,s[j] >= f[i]。
想法證明
上述思路的第一步是在最大相容活動子集合中加入最早結束的活動(以下稱第一個活動)。實際上,總存在一個最優安排,其包含第一個活動。
證明如下:
設 E ={0,1,2,…,n-1}為所給的活動集合。由於 E 中活動安排安結束時間的非減序排列,所以活動 1 具有最早完成時間。首先證明活動安排問題有一個最優解以貪心選擇開始(選擇了活動 1)。設 A 是所給的活動安排問題的一個最優解,且 B 中活動也按結束時間非減序排列,A 中的第一個活動是活動 k。若 k = 1,則 A 就是一個以貪心選擇開始的最優解。若 k > 1,則我們設 B = A -{k}∪{1}。由於 end[1] ≤ end[k](非減序排列),且 A 中活動是互為相容的,故 B 中的活動也是互為相容的。又由於 B 中的活動個數與 A 中活動個數相同,且 A 是最優的,故 B 也是最優的。也就是說 B 是一個以貪心選擇活動 1 開始的最優活動安排。因此,證明了總存在一個以貪心選擇開始的最優活動安排方案,也就是算法具有貪心選擇性質。
Java代碼
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class ActivityArrangement {
public static void main(String[] args) {
ArrayList<Time> list = new ArrayList<>();
list.add(new Time(3, 5));
list.add(new Time(1, 4));
list.add(new Time(5, 7));
list.add(new Time(0, 6));
list.add(new Time(6, 10));
list.add(new Time(3, 8));
list.add(new Time(5, 9));
list.add(new Time(8, 12));
list.add(new Time(8, 11));
list.add(new Time(2, 13));
list.add(new Time(12, 14));
//將數組按照結束時間排序
Collections.sort(list, new Comparator<Time>(){
@Override
public int compare(Time t1, Time t2) {
return t1.end - t2.end;
}
});
//選出局部最優解,返回結果數組
boolean[] r = greedySelector(list);
System.out.print("被安排上的活動為:");
for(int i = 0; i < list.size(); i++)
{
if(r[i] == true)
System.out.print("[" + list.get(i).start + ", "
+ list.get(i).end + "] ");
}
System.out.println();
}
/**
* 利用貪心性質選出活動安排的最優解
* @param list 存儲活動的列表
* @return r 最終返回的結果數組
*/
public static boolean[] greedySelector(ArrayList<Time> list) {
int n = list.size();
//存儲結果的數組
boolean[] r = new boolean[n];
//將第一個活動放入活動表中
r[0] = true;
//記錄最近一次加入到r中的活動
int j = 0;
//依次檢查活動i是否與當前已選擇的活動相容
for(int i = 1; i < n; i++) {
if(list.get(i).start >= list.get(j).end) {
r[i] = true;
j = i;
}
else
r[i] = false;
}
return r;
}
}
class Time {
public int start;
public int end;
public Time(int start, int end) {
this.start = start;
this.end = end;
}
}
運行結果

