第1題 帶時限的作業排序問題
問題描述:
設有一個單機系統、無其它資源限制且每個作業運行相等時間,不妨假定每個作業運行 1 個單位時間。現有 n 個作業,每個作業都有一個截止期限di>0,di 為整數。如果作業能夠在截止期限之內完成,可獲得 pi>0 的收益。問題要求得到一種作業調度方案,該方案給出作業的一個子集和該作業子集的一種排列,使得若按照這種排列次序調度作業運行,該子集中的每個作業都能如期完成,並且能夠獲得最大收益。
輸入:
第一行輸入 n 的值,以下 n 行輸入作業號 i,收益 pi,截止期限 di。
輸出:
n 個作業的一個最優子集。
示例 :
輸入:
4
1 100 2
2 10 1
3 15 2
4 27 1
1 4
輸出: 0 0 1 1
第2題 帶時限的作業排序問題II
問題描述:
帶時限的作業排序問題可描述為:設有 n 個作業和一台處理機,每個作業所需的處理時間、要求的時限和收益可用三元組(pi, di, ti),0<=i<n 表示,其中,作業 i 的所需時間為 ti,如果作業 i 能夠在時限 di 內完成,將可收益 pi,求使得總收益最大的作業子集 J。
輸入:
第一行輸入 n 的值;第二行輸入 pi;第三行輸入 di;第四行輸入 ti (i=0,…,n-1 且作業已經按時限非減次序排列)。
輸出:
xi(用固定長度 n-元組 xi 表示,xi=0 或 1,i=0,…,n-1)。
示例 :
輸入:
4
5 3 6 10
1 1 2 3
1 1 1 2
輸出: 0 0 1 1
第1題
思路分析
這題的思路比較明顯,就是每次都選取作業收益最大的,並把它放在允許的最大的截止時間內完成,符合貪心算法的基本思想。將輸入的每個作業按收益從大到小進行排序,用一個數組vis表示某時刻是否已經有作業正在運行,vis[i]=1,表示時間i被占用 。然后從第1個作業開始往后搜索,將該作業安排到vis[d]時間運行,如果d已經有安排,將從d-1開始往前搜索,直到找到一個未被占用的時間點。如果找不到空時間點,則跳過此作業,繼續向后搜索直到所有作業都搜索完。
1 #include <iostream> 2 #include <stdio.h> 3 #include <algorithm> 4 #include <string.h> 5 6 using namespace std; 7 8 #define MAXN 1100 9 10 struct Node 11 { 12 int id; //編號 13 int p; //收益 14 int d; //截止期限 15 }node[MAXN]; 16 17 int n; 18 int vis[MAXN]; //第i個單位時間是否有作業在運行 19 vector<int> res; //保存結果 20 21 bool cmp(Node a, Node b) 22 { 23 return a.p > b.p; 24 } 25 26 int main() 27 { 28 scanf("%d", &n); 29 for(int i = 0; i < n; ++i) 30 scanf("%d%d%d", &node[i].id, &node[i].p, &node[i].d); 31 //按照收益從大到小排序 32 sort(node, node+n, cmp); 33 memset(vis, 0, sizeof(vis)); 34 35 for(int i = 0; i < n; ++i) 36 { 37 // 如果到期時間沒有被占用,則直接占用到期時間; 38 // 否則,繼續往前找一個未被占用的時間 39 if(!vis[node[i].d]) 40 { 41 vis[node[i].d] = 1; 42 res.push_back(node[i].id); 43 } 44 else 45 { 46 for(int j = node[i].d-1; j >= 1; --j) 47 { 48 if(!vis[j]) 49 { 50 res.push_back(node[i].id); 51 vis[j] = 1; 52 break; 53 } 54 } 55 } 56 } 57 58 for(int i = 0; i < res.size(); ++i) 59 printf("%d ", res[i]); 60 61 return 0; 62 }
復雜度分析
從代碼來看,sort函數排序是O(nlogn)的時間復雜度,最差情況下,d >= n,且每組數據的截止期限都是d時,每次都要往前找一個空的時間點,此時需要找1+2+3+…+n次,所以耗費的時間為O(n2)。由於各個子塊不是嵌套的而是順序的,所以時間復雜度取最高的那個,所以整體來說時間復雜度是O(n2)。
對於空間復雜度,vis[]數組的大小由最大截止日期d決定。所以空間復雜度是O(d)。
第2題
這題和前面這題很相似,但其實解法有很大的差異。這題如果還是按前面那題的貪心思想去解題,是做不出來的,很難找到貪心算法中的最優度量標准。這題是一道比較難解決的問題,我在網上也沒找到解決方法。后來詢問一些算法比較厲害的同學之后,發現這題其實就是個0-1背包問題。
解題策略就是先選出最大的截止時間dmax,然后從1到dmax,進行0-1背包算法的操作。通過0-1背包的思路就可以找到最優解,這題相較於經典的0-1背包問題還是多了一些內容,那就是要記錄背包的路徑,最后輸出作業的子集的過程其實就是打印背包路徑的過程。
1 #include <iostream> 2 #include <stdio.h> 3 4 using namespace std; 5 6 int n; 7 int p[110], d[110], t[110]; 8 int memo[1110]; //memo[i]表示截止期限為i時的最大收益 9 int path[1110][1110]; //記錄背包路徑 10 11 //遞歸打印背包路徑 12 void printPath(int i, int j) 13 { 14 if(i < 0 || j < 0) 15 return; 16 17 if(path[i][j] == 1) 18 printPath(i-1, j-t[i]); 19 else 20 printPath(i-1, j); 21 22 printf("%d ", path[i][j]); 23 24 } 25 26 int main() 27 { 28 scanf("%d", &n); 29 int dmax = -1; //最大截止時間 30 for(int i = 0; i < n; ++i) 31 scanf("%d", &p[i]); 32 for(int i = 0; i < n; ++i) 33 scanf("%d", &d[i]); 34 35 //題中已經說明輸入按時限非減次序排列,所以d[n-1]就是最大的截止期限 36 dmax = d[n-1]; 37 38 for(int i = 0; i < n; ++i) 39 scanf("%d", &t[i]); 40 41 for(int i = 0; i <= dmax; ++i) 42 { 43 if(i >= t[0]) 44 { 45 memo[i] = p[0]; 46 path[0][i] = 1; //記錄路徑 47 } 48 else 49 memo[i] = 0; 50 } 51 52 for(int i = 1; i < n; ++i) 53 { 54 for(int j = dmax; j >= t[i]; --j) 55 { 56 if(memo[j] < p[i] + memo[j-t[i]]) 57 { //物品裝入背包,記錄路徑 58 memo[j] = p[i] + memo[j-t[i]]; 59 path[i][j] = 1; //記錄路徑 60 } 61 } 62 } 63 64 //遞歸打印背包路徑 65 printPath(n-1, dmax); 66 67 return 0; 68 }
復雜度分析
從代碼來看,這個程序運行的時間主要消耗在第一次的雙重循環中,外層循環的次數為n,內層循環次數為dmax,所以這個算法的平均時間復雜度為O(n*dmax)。