经典问题-活动选择问题-DP和贪心

 

经典问题-活动选择问题-DP和贪心

分类: 算法和数据结构学习   316人阅读  评论(0)  收藏  举报

活动选择问题:

Given a set S of n activities with and start time, Si and fi, finish time of an ith activity. Find the maximum size set of mutually compatible activities.

 

 

 

 

兼容活动概念:

Activities and j are compatible if the half-open internal [si, fi) and [sj, fj) do not overlap, that is, i and j are compatible ifsi ≥ fj  and s≥  fi

 

算法导论上提供了两种方法,一种是DP方法, 另一种是贪心算法,同时网上还提供了另一种DP的方法。下面对三种方法进行讲解:

1,算法导论上的DP方法:

先对每个活动ai按照活动结束时间fi从小到大排序,之后按照递归方程进行动态规划

[cpp]  view plain copy
  1. /*动态规划解法- 算法导论上的思路  
  2. S(i,j)={ak,fi<=sk<fk<=sj}表示在活动ai结束之后,在aj开始之前的活动集,则整个问题空间可表示为S(0,n+1), 
  3. 其中添加活动a0和an+1,s0=f0=0,sn+1=fn+1=2^32-1 
  4. 根据dp[i][j]的含义,假设S(i,j)中不包含任何的活动序列(即满足S(i,j)定义的活动不存在),则dp[i][j]=0; 
  5. 否则,假设ak时S(i,j)中存在的一个兼容活动,那 
  6. 么这里存在问题S(i,j)的最优子结构:S(i,k)和S(k,j). 
  7. 根据上面叙述,可以定义问题的递归解结构: 
  8. dp[i][j]=0,如果S(i,j) =NULL  
  9. dp[i][j]=max{dp[i][k]+dp[k][j]+1},i<k<j 
  10. 最后要求的结果是dp[0][n+1]  
  11. 需要注意的是: 在对区间队列进行动态规划之前,要对区间的结束时间按照时间从小到大进行排序,保证f1 <= f2 <= f3 <= ...<= fn  
  12. */  
  13. #include <iostream>  
  14. #define MAX 105  
  15. using namespace std;  
  16. struct interval  
  17. {  
  18.        int s;  
  19.        int f;  
  20.        int p;  
  21. }inter[MAX];  
  22. int cmp(const void* a, const void* b)  
  23. {  
  24.     return ((interval*)a)->f - ((interval*)b)->f;  
  25. }  
  26. int N;  
  27. int dp[MAX][MAX];  
  28. int tracert[MAX][MAX];  
  29. int output[MAX];  
  30. void ACTIVITY_SELECTION_DYNAMIC()  
  31. {  
  32.      int i, j, k, l;  
  33.      for(i = 0; i<= N+1; i++)  
  34.           dp[i][i] = 1;  
  35.      dp[0][0] = dp[N+1][N+1] = 0;  
  36.      for(l = 1; l<= N+1; l++)  
  37.      {  
  38.            for(i = 0; i<=N+1-l; i++)  
  39.            {  
  40.                  j = i+l;  
  41.                  dp[i][j] = 0;  
  42.                  tracert[i][j] = -1;  
  43.                  for(k = i+1; k < j; k++)  
  44.                  {  
  45.                        if(inter[k].s >= inter[i].f && inter[k].f <= inter[j].s)  
  46.                        {  
  47.                                      if(dp[i][k] + dp[k][j] + 1 > dp[i][j])  
  48.                                      {     dp[i][j] = dp[i][k] + dp[k][j] + 1;  
  49.                                            tracert[i][j] = k;  
  50.                                      }  
  51.                        }  
  52.                  }  
  53.                    
  54.            }  
  55.      }  
  56. }  
  57. void printout(int s, int f)  
  58. {  
  59.      if(tracert[s][f] == -1) return;  
  60.        
  61.      cout << tracert[s][f] << ' ';  
  62.        
  63.      printout(s, tracert[s][f]);  
  64.      printout(tracert[s][f], f);  
  65. }  
  66. int main()  
  67. {  
  68.     cin >> N;  
  69.     int i, j, k;  
  70.     inter[0].s = inter[0].f = 0;  
  71.     inter[0].p = 1;  
  72.     for(i = 1; i<= N; i++)  
  73.          cin >> inter[i].s;  
  74.     for(i = 1; i<= N; i++)  
  75.     {     cin >> inter[i].f;  
  76.           inter[i].p = 1;  
  77.     }  
  78.     inter[N+1].s = inter[N+1].f = (2<<29)-1;  
  79.     inter[N+1].p = 1;  
  80.     //要对输入的区间队列按照结束的时间f从小到大的排序,这是必须的   
  81.     qsort(inter, N+2, sizeof(interval), cmp);     
  82.       
  83.     ACTIVITY_SELECTION_DYNAMIC();  
  84.     cout << dp[0][N+1] << endl;  
  85.     printout(0, N+1);  
  86.     cout << endl;  
  87.       
  88.     return 0;  
  89. }  
 

2. 算法导论上的贪心算法:

先对每个活动ai按照活动结束时间fi从小到大排序,每次选择最早结束的活动,

而这个活动的选择需要与前面所选的活动相容。

原始问题是S= S[0,n+1],设活动am1是S[0,n+1]中具有最早结束时间的活动,

下一个问题是S[m1, n+1],设选择活动am2为S[m1,n+1]中最早结束时间的活动,

则下一个子问题是S[m2,n+1],如此继续:

[cpp]  view plain copy
  1. #include <iostream>  
  2. #define MAX 105  
  3. using namespace std;  
  4. struct interval  
  5. {  
  6.        int s;  
  7.        int f;  
  8.        int p;  
  9. }inter[MAX];  
  10. int cmp(const void* a, const void* b)  
  11. {  
  12.     return ((interval*)a)->f - ((interval*)b)->f;  
  13. }  
  14. int N;  
  15. int tracert[MAX]; //如果tracert[i] = 1表示活动i被选择,如果=0表示不选择   
  16. int ACTIVITY_SELECTION_GREEDY()  
  17. {  
  18.      memset(tracert, 0, N);  
  19.      tracert[0] = 1;  
  20.      int i, m;  
  21.      m = 1;  
  22.      int count= 1;  
  23.      for(int i = 1; i< N; i++)  
  24.      {  
  25.            if(inter[i].s >= inter[m].f)  
  26.            {  
  27.                 tracert[i] = 1;  
  28.                 m = i;  
  29.                 count ++;  
  30.            }    
  31.      }  
  32.      return count;  
  33. }  
  34. void printout()  
  35. {  
  36.      int i, j;  
  37.      for(i = 0; i< N; i++)  
  38.           if(tracert[i] == 1)  
  39.               cout << i+1 << ' ';  
  40.      cout << endl;  
  41. }  
  42. int main()  
  43. {  
  44.     cin >> N;  
  45.     int i, j, k;  
  46.     for(i = 0; i< N; i++)  
  47.          cin >> inter[i].s;  
  48.     for(i = 0; i< N; i++)  
  49.     {     cin >> inter[i].f;  
  50.           inter[i].p = 1;  
  51.     }  
  52.       
  53.     qsort(inter, N, sizeof(interval), cmp);  
  54.       
  55.     cout << ACTIVITY_SELECTION_GREEDY()  << endl;  
  56.       
  57.     printout();  
  58.       
  59.     return 0;  
  60. }  
 


3. 网上提供的DP方法:

 

Before starting the dynamic programming algorithm itself, we do some precomputation, as follows. We first sort the activities according to fi nish time, so that f1 <=f2 <=.. <= fn. We also compute, for every activity i, a number H(i) defi ned as H(i) = max{l<=i-1&& l>=0 && fl<=si} .the maximum value of the empty set is 0. We can compute each value H(i) in time O(log n) using binary search, so all of the precomputation can be done in time O(n log n).

We now perform the four steps of the dynamic programming method.

Step 1:

Describe an array of values we want to compute.

For every integer i, 0 <=i<=n, de fine A(i) = the largest pro fit we can get by (feasibly) scheduling activities from {1; 2,.., i}.

The value we are ultimately interested in is A(n).

Step 2:

Give a recurrence.

A(0) = 0.

This is true because if we are not allowed to schedule any activities at all, then the highest pro fit we can make is 0.

Let 1 <=i <=n. Then A(i) = max{A(i-1); gi + A(H(i))}:

[cpp]  view plain copy
  1. /* 
  2. 动态规划思路: 
  3. 用数组A[i] 表示活动0->i范围内的最大兼容活动数  
  4. A[0] = 0; 
  5. A[i] = max{A[i-1], gi+A[H[i]]} 0<i <= n; 其中H[i]表示在0->n-1所有活动中与活动i兼容的活动的最大下标     
  6. 最后结果A[n];  
  7. */  
  8. #include <iostream>  
  9. #define MAX 105  
  10. using namespace std;  
  11. struct interval  
  12. {  
  13.        int s;  
  14.        int f;  
  15.        int p;  
  16. }inter[MAX];  
  17. int cmp(const void* a, const void* b)  
  18. {  
  19.     return ((interval*)a)->f - ((interval*)b)->f;  
  20. }  
  21. int N;  
  22. int A[MAX];  
  23. int tracert[MAX];  
  24. void ACTIVITY_SELECTION_DYNAMIC1()  
  25. {  
  26.     memset(A, 0, N);  
  27.     memset(tracert, 0, N);  
  28.     int i, j, k;  
  29.     for(i = 1; i< N; i++)  
  30.     {  
  31.           for(j = i-1; j >= 0; j--)  
  32.                 if(inter[j].f <= inter[i].s) break;  
  33.           A[i] = max(A[i-1], 1+A[j]);  
  34.     }  
  35. }  
  36. void printout()  
  37. {  
  38.      int i = N-1, j;  
  39.      while(i >= 0)  
  40.      {  
  41.              if(A[i] == A[i-1])  
  42.              {    
  43.                  tracert[i] = 0;  
  44.                  i--;  
  45.              }  
  46.              else  
  47.              {  
  48.                  tracert[i] = 1;  
  49.                  for(j = i-1; j>= 0; j--)  
  50.                       if(inter[j].f <= inter[i].s) break;  
  51.                  i = j;  
  52.              }  
  53.      }  
  54.      for(i = 0; i< N; i++)  
  55.          if(tracert[i] == 1)  
  56.               cout << i+ 1 << ' ';  
  57.      cout << endl;  
  58. }  
  59. int main()  
  60. {  
  61.     cin >> N;  
  62.     int i, j, k;  
  63.     for(i = 0; i< N; i++)  
  64.          cin >> inter[i].s;  
  65.     for(i = 0; i< N; i++)  
  66.     {     cin >> inter[i].f;  
  67.           inter[i].p = 1;  
  68.     }  
  69.       
  70.     qsort(inter, N, sizeof(interval), cmp);  
  71.       
  72.     ACTIVITY_SELECTION_DYNAMIC1();   
  73.     cout << A[N-1] << endl;  
  74.     printout();  
  75.       
  76.     return 0;  
  77. }  
 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值