1. 什么是贪心算法
对于最优解问题,选取某种度量标准,将n个输入按度量标准排序,并按次序一次输入一个值。若当前输入和以前在这种度量标准下的部分解合并在一起能够构成可行解,则将其与部分解合并,否则则去掉这个输入。重复上述过程直到 枚举结束。这种通过某种度量标准获取最优解的分级处理方法成为贪心算法。
SPARKS语言描述:
procedure GREEDY(A, n)
solution ← ∅
for i ← 1 to n do
x ← SELECT(A)
if FEASIBLE(solution, x) then
solution ← UNION(solution, x)
endif
repeat
return(solution)
end GREEDY
2. 贪心算法解决部分背包问题
SPARKS语言描述:
procedure GREEDY_KNAPSACK(P, W, M, X, n)
// P(1:n)和W(1:n)分别含有按P(i)/W(i)≥P(i+1)/W(i+1)排序的n件物品的效益值和重量。
// M是背包的容量大小,X(1:n)是解向量
real P(1:n), W(1:n), X(1:n), M, cu
integer i, n
X ← 0
cu ← M
for i ← 1 to n do
if cu < W(i) then
exit
endif
cu ← cu - W(i)
X(i) ← 1
repeat
if i <= n then
X(i) ← cu / W(i)
end if
return(X)
end GREEDY_KNAPSACK
C++实现:
#include <queue>
#include <iostream>
#include <stdio.h>
class coin {
private:
int _weight;
int _value;
double _ratio;
public:
coin(int w, int v) : _weight(w), _value(v), _ratio((double)v / w) {}
double get_ratio() { return _ratio; }
double get_weight() { return _weight; }
double get_value() { return _value; }
};
class cmp {
public:
bool operator()(coin c1, coin c2) {
return c1.get_ratio() < c2.get_ratio();
}
};
void p2240() {
int N(0), T(0), w(0), v(0);
double s(0.0);
std::cin >> N >> T;
std::priority_queue<coin, std::vector<coin>, cmp> coins;
for (int i = 0; i < N; ++i) {
std::cin >> w >> v;
coins.push(coin(w, v));
}
coin t = coins.top();
coins.pop();
while (true) {
if (t.get_weight() <= T) {
T -= t.get_weight();
s += t.get_value();
if (!coins.empty()) {
t = coins.top();
coins.pop();
}
else break;
}
else {
s += T * t.get_ratio();
break;
}
}
printf("%.2lf\n", s);
}
3. 最优解证明
一般证明方法:
- 设计问题最优解的一般形式
- 比较贪心解与最优解
2.1 若最优解与贪心解相同,则贪心解就是最优解
2.2 若最优解与贪心解不同- 找出首次出现不同的位置i
- 用贪心解xi替换最优解的xi
- 设法保证在替换完成后仍不违反约束条件
- 设法证明替换完成后的新解的效益值至少不比原来的解更差
- 重复2.2直到替换后的最优解与贪心解相同
- 因为在上述代换改变前后,最优解的效益值没有任何损失,因此贪心解可以作为最优解
部分背包问题贪心算法的证明: