深入理解動態規劃的一系列問題(10)
不知不覺已經寫到第10個問題了,動態規劃的問題相信大家也發現了一些規律,看到什么樣的問題知道動態規劃求解呢?基本上所有的問題都類似最短路,定義了一個狀態后根據狀態列動態規劃方程,方程的形式一般是遞歸式,包含一個子結構的更新過程……接下來就是羅列問題了:第10題是算導中提過的題目,DEADLINE調度問題(其實這個問題還有一個高階版本)。
deadline問題的描述是:一臺處理器可以處理n個作業,表示為a1,a2,…,an。每個作業aj有相等的運行時長(如果不等也作為變量呢?),有效益pj以及截止時間dj。機器是串行的,也就是說一個時刻只能處理一個作業。而且作業是原子的,不可拆分。獲得效益pj的前提是aj一定要在dj之前完成。請給出一個算法來達到最優調度,即效益最大化。之前講到,在看到最優化問題時就直接想DP求解,這個當然是個好習慣,但是這里再介紹一個思路就是優先可以先考慮貪心是否可解,相對列動態規劃方程定義狀態這些事情,貪心往往更直觀更簡單(人心不足啊)。
這個問題是可以使用貪心法求解的,貪心算法不是我們這個系列問題的關鍵,但是也可以簡單談一下貪心:貪心的核心是要確定正確的貪心目標,就是在一系列決策做出時,每個步驟選擇的理由是什么,比如這個問題,是按照處理時間排序后貪心?還是按照效益排序后貪心?根據目標是效益最大化,那么就可以做出正確選擇了。
當然動態規劃比貪心優勢的一點就是貪心是個局部策略,不是所有問題都能得到全局最優解,而DP是全局最優的,使用DP總能得到最優決策。針對本題,我們思考如何定義一個狀態來列出狀態轉移方程。就像考慮其他類似組合優化問題時,看到有決策性步驟問題時,我們定義狀態是帶有步驟標號的,也就是第一章提到的stage decision,每個stage用k表示,到了第i個stage后,剩余的可決策集S作為另一個狀態元素,于是我們定義狀態(k,S),其中k是stage,S是表示當前狀態下剩余可選決策。為了保證S集是可選擇的決策集,我們需要對deadline排序,即按照截止時間排序。這樣,我們列DPFE如下:f(k,S)=max d∈S{c(d|s)+f(k+1,S-wmqeeuq)},其中c(d|s)=pd,基本條件是f(k,S)=0,當k=1+N或者S為空時。
以具體數據為例:5個作業標號為{0,1,2,3,4},其中每個作業的效益為{10,15,20,1,5},完成deadline為{1,2,2,3,3},求最優策略的效益。可以看到按照之前貪心解法,按照效益排序后,為20,15,10,5,1,而對應的截止時間也變為2,2,1,3,3,這樣最優解就是20+15+5=40。DP的解法如下源碼:
1: package com.jybat.dp;
2:
3: import java.util.Set;
4: import java.util.SortedSet;
5: import java.util.TreeSet;
6:
7: //deadline scheduling of unit-time jobs
8: public class DEADLINE {
9:
10: private static int[] profit = { 10, 15, 20, 1, 5 };
11: private static int[] deadline = { 1, 2, 2, 3, 3 }; // sorted
12: private static int N = profit.length; // no.of jobs
13:
14: private static SortedSet<Integer> setOfAllJobs = new TreeSet<Integer>();
15:
16: public DEADLINE() {
17: for (int i = 0; i < N; i++) {
18: setOfAllJobs.add(i);
19: }
20: }
21:
22: private static boolean feasible(SortedSet<Integer> jobs, int k, int d) {
23: int j = 0;
24: for (int i = 0; i < N; i++) {
25: if (!(jobs.contains(new Integer(i))) || i == d) {
26: // if i already chosen or next (and is j-th),
27: // does it meet its deadline?
28: if (deadline[i] < ++j) {
29: return false;
30: }
31: }
32: }
33: return true;
34: }
35:
36: private static int cost(SortedSet<Integer> jobs, int k, int d) {
37: if (feasible(jobs, k, d)) {
38: return profit[d];
39: } else {
40: return 0;
41: }
42: }
43:
44: // jobs not yet chosen at stage k
45: public int f(SortedSet<Integer> jobs, int k) {
46: if (k > N)
47: return 0;
48: int max = Integer.MIN_VALUE;
49: for (int d : jobs) {
50: SortedSet<Integer> nJobs = copySet(jobs);
51: nJobs.remove(d);
52: int t = cost(jobs, k, d) + f(nJobs, k + 1);
53: if (t > max)
54: max = t;
55: }
56: return max;
57: }
58:
59: private SortedSet<Integer> copySet(SortedSet<Integer> jobs) {
60: SortedSet<Integer> nJobs = new TreeSet<Integer>();
61: for (int i : jobs) {
62: nJobs.add(i);
63: }
64: return nJobs;
65: }
66:
67: /**
68: * @param args
69: */
70: public static void main(String[] args) {
71: DEADLINE d = new DEADLINE();
72: System.out.println(d.f(setOfAllJobs, 1));
73: }
74:
75: }
posted on 2014-05-12 16:54 changedi 閱讀(2652) 評論(1) 編輯 收藏 所屬分類: 算法