此题是算法导论贪心算法的16.1-5题。
问题描述:
考虑一个活动选择问题的一个变形:每个活动ai除了开始和结束时间外,还有一个值vi。目标不再是求规模最大的兼容活动子集,而是求值之和最大的兼容活动子集。也就是说,选择一个兼容活动子集A,是的vk(k属于A)之和最大化。设计一个多项式时间的算法求解此问题。
思路:
此题是活动安排问题的一个变形。最优化目标不再是最大兼容活动子集,所以不能用常规的活动安排问题的贪心方案。
考虑这样一个问题,我们对与每个活动i,都可以考虑这个活动i是否被加入到最优活动子集中。所以opt[i]可以从这两中情况得来。
opt[i] = max(opt[i-1], opt(pre(i)+vi));
上述公式需要建立在活动已排序的基础上。活动按照结束时间升序排序。不选择第i个活动时,opt[i] = opt[i-1]; 选择第i个活动时opt[i] = opt(pre(i) + vi); pre(i)表示i活动开始之前结束的活动。根据这个公式我们可以轻松的写出动态规划的代码。
该方法最坏情况下时间复杂度为O(n^2);最好情况下为O(n);
代码:
#include <iostream> //c++
#include <algorithm>
#include <vector>
using namespace std;
typedef struct activity{
int start;
int end;
int value;
}Activity;
bool myfunction (Activity a, Activity b) {
return (a.end < b.end);
}
int getMostProfit(vector<Activity> activities){
if(activities.size() == 0)
return 0;
sort(activities.begin(), activities.end(),myfunction);
int opt[activities.size()];
opt[0] = activities[0].value;
for(int i = 1; i < activities.size(); i++){
int j;
for(j = i-1; j >=0; j--){
if(activities[j].end <= activities[i].start)
break;
}
if(j >=0)
opt[i] = ((opt[i-1]>opt[j]+activities[i].value)?opt[i-1]:(opt[j]+activities[i].value));
else opt[i] = ((opt[i-1]>activities[i].value)?opt[i-1]:activities[i].value);
}
for(int i = 0; i < activities.size(); i++)\
cout << opt[i] << endl;
return opt[activities.size()-1];
}
int main(){
int starts[] = {1,3,0,5,3,5,6,8,8,2,12};
int ends[] ={4,5,6,7,9,9,10,11,12,14,16};
int values[] = {1,2,4,6,4,2,3,3,4,1,3};
vector<Activity> activities;
for(int i = 0; i < 11 ; i++){
Activity tmp;
tmp.start = starts[i];
tmp.end = ends[i];
tmp.value = values[i];
activities.push_back(tmp);
}
int profit = getMostProfit(activities);
cout << "mostProfit is "<<profit << endl;
}
参考:《算法导论》