1、背包问题(物品可以分割)
给定n种物品和一个容量为C的背包,物品i的重量是wi,其价值为vi,背包问题是如何选择装入背包的物品,使得装入背包中物品的总价值最大?
应用第三种贪心策略,每次从物品集合中选择单位重量价值最大的物品,如果其重量小于背包容量,就可以把它装入,并将背包容量减去该物品的重量,然后我们就面临了一个最优子问题——它同样是背包问题,只不过背包容量减少了,物品集合减少了。因此背包问题具有最优子结构性质。
算法7.6的时间主要消耗在将各种物品依其单位重量的价值从大到小排序。因此,其时间复杂性为O(nlog2n)。
//假设物品已按单位重量降序排列
//背包容量C,物品重量w[],物品价值v[]
int KnapSack(int w[], int v[], int n, int C) {
double x[10] = { 0 }; //物品可部分装入
int maxValue = 0;
int i;
for (i = 0; w[i] < C; i++) {
x[i] = 1; //将物品i装入背包
maxValue += v[i];
C = C - w[i]; //背包剩余容量
}
x[i] = (double)C / w[i]; //物品i装入一部分
maxValue += x[i] * v[i];
return maxValue; //返回背包获得的价值
}
2、活动安排问题
设有n个活动的集合E={1, 2, …, n},
其中每个活动都要求使用同一资源(如演讲会场),而在同一时间内只有一个活动能使用这一资源。
每个活动i都有一个要求使用该资源的起始时间si和一个结束时间fi,且si <fi 。
活动安排问题要求在所给的活动集合中选出最大的相容活动子集。
由于活动占用资源的时间没有限制,因此,后一种贪心选择更为合理。
为了在每一次贪心选择时快速查找具有最早结束时间的相容活动,先把n个活动按结束时间非减序排列。这样,贪心选择时取当前活动集合中结束时间最早的活动就归结为取当前活动集合中排在最前面的活动。
//起始时间s[],结束时间f[],存储活动安排的信息B[],按结束时间的非减序排列
int ActiveManage(int s[], int f[], int B[], int n) {
int i, j, count;
B[0] = 1; //安排活动1
j = 0; count = 1;
for (i = 1; i < n; i++) { //依次考察每一个活动
if (s[i] > f[j]) {
B[i] = 1;
j = i;
count++;
}
else {
B[i] = 0;
}
}
return count; //返回已安排的活动个数
}
3、多级调度问题
设有n个独立的作业{1, 2, …, n},由m台相同的机器{M1, M2, …, Mm}进行加工处理,作业i所需的处理时间为ti(1≤i≤n),每个作业均可在任何一台机器上加工处理,但不可间断、拆分。多机调度问题要求给出一种作业调度方案,使所给的n个作业在尽可能短的时间内由m台机器加工处理完成。
例:设7个独立作业{1, 2, 3, 4, 5, 6, 7}由3台机器{M1, M2, M3}加工处理,各作业所需的处理时间分别为{2, 14, 4, 16, 6, 5, 3}。贪心法产生的作业调度如下:
//n个作业的处理时间t[],m台机器的空闲时间d[]
//先将t[]从大到小排序
void MultiMachine(int t[], int n, int d[], int m) {
int S[3][7]; //假设3台机器处理7个作业
int rear[3]; //S[i]为存储机器i处理作业的队列,rear[i]为队尾下标
int i, j, k;
for (i = 0; i < m; i++) { //安排前m个作业
S[i][0] = i;
rear[i] = 0; //每个作业对列均只有一个作业
d[i] = t[i];
}
for (i = m; i < n; i++) { //依次安排余下的每一个作业
for (j = 0, k = 1; k < m; k++) { //寻找最先空闲的机器
if (d[k] < d[j]) {
j = k; //j为最先空闲的机器序号
}
}
rear[j]++; S[j][rear[j]] = i; //将i插入队列S[j]中
d[j] = d[j] + t[i];
}
for (i = 0; i < m; i++) { //输出每个机器处理的作业
cout << "机器" << i << ":";
for (j = 0; S[i][j] >= 0; j++) {
cout << "作业" << S[i][j] << " ";
}
cout << endl;
}
}