活动选择的贪心算法与动态规划
問題:
? ? ? 有一個需要使用每個資源的n個活動組成的集合S=?{a1,a2,···,an?},資源每次只能由一個活動使用。每個活動a都有一個開始時間和結束時間,且?0<=?s?<?f 。一旦被選擇后,活動a就占據半開時間區(qū)間[s,f]。如果[s,f]和[s,f]互不重疊,則稱兩個活動是兼容的。該問題就是要找出一個由互相兼容的活動組成的最大子集。
? ? ? 定義子集合Sij?=?{?ak??S?:?f?i?<=?sk?<?f?k?<= s?j},?即每個活動都在ai結束之后開始,在aj開始之前結束,亦即Sij包含了所有和ai和aj兼容的活動。?
? ? ? 假設S中的活動已按照結束時間遞增的順序排列,則Sij具有如下的性質:
? ? ? 1.當i?<=?j時,Sij?為空,
? ? ? 2.假設ak屬于Sij,那么ak將把Sij分解成兩個子問題,Sij包含的活動集合就等于Sik中的活動+ak+Skj中的活動。從這里就可以看出Sij的最優(yōu)子結構性質:Sij的最優(yōu)解包含了子問題Sik和Skj的最優(yōu)解。假設Sij的最大兼容活動子集為Aij,那么有Aij?=?Aik?U??ak?U?Akj。整個活動選擇問題的最優(yōu)解也是S0,n+1的解。
? ? ? 假設c[i,j]為Sij中最大兼容子集中的活動數。則有如下遞歸式:
??????C[i,j]?= 0???????????????????????如果?Sij?為空
??????????????????????C[i,j] =?max{c[i,k]?+?c[k,j]?+1?} ? ? ? ? ? ? ? ? ? i?<?k?<?j?&?ak??Sij??如果Sij?不為空
根據這個遞歸式,可以得到一個動態(tài)規(guī)劃解法。
// greedy_algorithm.cpp : 定義控制臺應用程序的入口點。 //#include "stdafx.h" #include<iostream> #include<queue> using namespace std;#define NofActivity 11 //有效的活動數 int c[NofActivity + 2][NofActivity + 2]; //c[i][j]存放第i個結束后第j個開始前兼容的活動個數 int reme[NofActivity + 2][NofActivity + 2]; //記錄哪幾個活動滿足條件 //活動的結構/ struct Activity {int num; //活動的標號int start; int finish; }; //活動已經按結束時間的早晚排好序 //初始化活動結構數組,注意添加了了頭和尾,活動有效數據只有11組,添加到13組的意思是找第0組結束之后,第12組開始之前可以兼容的那些活動組合 Activity Act[NofActivity+2] = { {0,0,0},{ 1,1,4 },{2,3,5 },{3, 0,6 },{4, 5,7 },{5, 3,9 },{6, 5,9 },{7, 6,10 },{8, 8,11 },{9, 8,12 },{10, 2,14 },{11, 12,16 }, {12,24,24}};///用隊列來存儲符合條件的活動,遞歸版本// queue<Activity> select; void Recursive_activity_selector(Activity* Act, int k, int n) {//查找k結束之后第一個結束的活動int m = k + 1;while (m <= n&&Act[m].start < Act[k].finish)m++;//如果找到就把它入隊,遞歸調用查找下一個if (m <= n){select.push(Act[m]);Recursive_activity_selector(Act, m, n);} }///活動選擇的迭代版本/ void Greedy_activity_selector(Activity* Act) {//先把第一個入隊int n = NofActivity;while (!select.empty())select.pop();select.push(Act[1]);//循環(huán)查找下一個滿足條件的活動入隊int k = 1;for (int i = 2; i <= n; i++){if (Act[i].start > Act[k].finish) {select.push(Act[i]);k = i;}} }/活動選擇的動態(tài)規(guī)劃版本// void activity_selector(Activity* Act) {//初始化for (int i = 0; i <= NofActivity+1; i++){for (int j = 0; j <= NofActivity + 1; j++){c[i][j] = 0;reme[i][j] = 0;}}//從長度為2的開始查找for(int l=2;l<=NofActivity+2;l++)for (int i = 0; i <= NofActivity-l+3; i++) //注意開始要從虛擬的活動0開始,到虛擬的活動NofActivity+1結束,這是由c[i][j]的定義所決定的{int j = i + l - 1;bool flag=false; //標志i,j之間是否含有兼容的活動for (int k = i + 1; k < j; k++){if (Act[k].start > Act[i].finish&&Act[k].finish < Act[j].start) //c[i][j]定義的條件{if (c[i][j] < c[i][k] + c[k][j] + 1){c[i][j] = c[i][k] + c[k][j] + 1;//注意這里reme[i][j]的賦值,因為c[i][j] < c[i][k] + c[k][j] + 1,是小于號而不是小于等于,所以reme[i][j]會記錄第一個滿足條件的k,如reme[0][12]=1;reme[i][j] = k;}flag = true; //有置1}}if (!flag)c[i][j] = 0;}//打印出c[i][j];for (int i = 0; i <= NofActivity + 1; i++) {for (int j = 0; j <= NofActivity + 1; j++)cout << c[i][j] << ' ';cout << endl;} }//打印所選擇的活動,act = reme[act][j]是由上訴對reme[i][j]的賦值規(guī)律所決定 void printSelect(int i, int j) {int act = reme[i][j];while (act){cout << act << '\t';act = reme[act][j];} }int main() {//Recursive_activity_selector(Act, 0, NofActivity);/*Greedy_activity_selector(Act);while (!select.empty()){cout << select.front().num<< '\t';select.pop();}*/activity_selector(Act);printSelect(0, 12);while (1);return 0; }
轉載于:https://www.cnblogs.com/linear/p/6680132.html
總結
以上是生活随笔為你收集整理的活动选择的贪心算法与动态规划的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 本机向windows服务器传输文件的三种
- 下一篇: 关于Element中的clientWid