课题摘要:
贪心算法(Greedy Algorithm)是一种在每一步选择中都采取当前状态下最优(即最有利)的选择,从而希望导致结果是全局最优的算法。贪心算法并不总是能得到最优解,但它在很多问题上都能得到较好的近似解,并且通常具有较高的效率。
贪心算法在每一步选择中都采取当前状态下最优的选择,从而希望导致结果是全局最优的。这种“最优”通常是根据某种局部标准来衡量的。
给定一组硬币面值和一个金额,求用最少的硬币数凑成该金额。
每次选择面值最大的硬币。
#include
#include
#include
int coin_change(const std::vector<int>& coins, int amount) {
std::vector<int> sorted_coins = coins;
std::sort(sorted_coins.rbegin(), sorted_coins.rend());
int count = 0;
for (int coin : sorted_coins) {
while (amount >= coin) {
amount -= coin;
count++;
}
}
return amount == 0 ? count : -1;
}
每次选择面值最大的硬币,直到金额为0。
给定一组活动的开始时间和结束时间,求最多可以安排多少个不冲突的活动。
每次选择结束时间最早的活动。
#include
#include
#include
int activity_selection(const std::vector<int>& start, const std::vector<int>& finish) {
std::vector<std::pair<int, int>> activities;
for (size_t i = 0; i < start.size(); ++i) {
activities.emplace_back(start[i], finish[i]);
}
std::sort(activities.begin(), activities.end(), [](const auto& a, const auto& b) {
return a.second < b.second;
});
int count = 0;
int last_finish = 0;
for (const auto& activity : activities) {
if (activity.first >= last_finish) {
count++;
last_finish = activity.second;
}
}
return count;
}
每次选择结束时间最早的活动,确保后续活动不冲突。
给定一组字符及其频率,求一种最优的编码方式,使得编码后的字符串长度最短。
每次选择频率最小的两个字符合并。
#include
#include
#include
#include
#include
struct Node {
char ch;
int freq;
Node* left;
Node* right;
Node(char ch, int freq) : ch(ch), freq(freq), left(nullptr), right(nullptr) {}
};
struct Compare {
bool operator()(Node* left, Node* right) {
return left->freq > right->freq;
}
};
void build_codes(Node* root, const std::string& prefix, std::unordered_map<char, std::string>& codes) {
if (!root) return;
if (root->ch != '\0') {
codes[root->ch] = prefix;
}
build_codes(root->left, prefix + "0", codes);
build_codes(root->right, prefix + "1", codes);
}
std::unordered_map<char, std::string> huffman_encoding(const std::unordered_map<char, int>& frequencies) {
std::priority_queue<Node*, std::vector<Node*>, Compare> pq;
for (const auto& entry : frequencies) {
pq.push(new Node(entry.first, entry.second));
}
while (pq.size() > 1) {
Node* left = pq.top(); pq.pop();
Node* right = pq.top(); pq.pop();
Node* merged = new Node('\0', left->freq + right->freq);
merged->left = left;
merged->right = right;
pq.push(merged);
}
std::unordered_map<char, std::string> codes;
build_codes(pq.top(), "", codes);
return codes;
}
int main() {
std::unordered_map<char, int> frequencies = {{'a', 5}, {'b', 9}, {'c', 12}, {'d', 13}, {'e', 16}, {'f', 45}};
auto codes = huffman_encoding(frequencies);
for (const auto& entry : codes) {
std::cout << entry.first << ": " << entry.second << std::endl;
}
return 0;
}
通过构建霍夫曼树,为每个字符分配最优的编码。
给定一个加权无向图,求一个最小生成树。
每次选择权重最小的边,确保不形成环。
#include
#include
#include
class UnionFind {
public:
UnionFind(int n) : parent(n) {
for (int i = 0; i < n; ++i) {
parent[i] = i;
}
}
int find(int x) {
if (parent[x] != x) {
parent[x] = find(parent[x]);
}
return parent[x];
}
void union_sets(int x, int y) {
int rootX = find(x);
int rootY = find(y);
if (rootX != rootY) {
parent[rootX] = rootY;
}
}
private:
std::vector<int> parent;
};
std::vector<std::tuple<int, int, int>> kruskal(const std::vector<std::tuple<int, int, int>>& edges, int n) {
std::vector<std::tuple<int, int, int>> mst;
UnionFind uf(n);
std::vector<std::tuple<int, int, int>> sorted_edges(edges.begin(), edges.end());
std::sort(sorted_edges.begin(), sorted_edges.end(), [](const auto& a, const auto& b) {
return std::get<2>(a) < std::get<2>(b);
});
for (const auto& edge : sorted_edges) {
int u = std::get<0>(edge);
int v = std::get<1>(edge);
int weight = std::get<2>(edge);
if (uf.find(u) != uf.find(v)) {
uf.union_sets(u, v);
mst.push_back(edge);
}
}
return mst;
}
int main() {
std::vector<std::tuple<int, int, int>> edges = {{0, 1, 2}, {1, 2, 3}, {2, 3, 1}, {3, 0, 4}, {0, 2, 5}};
int n = 4;
auto mst = kruskal(edges, n);
for (const auto& edge : mst) {
std::cout << std::get<0>(edge) << " - " << std::get<1>(edge) << " : " << std::get<2>(edge) << std::endl;
}
return 0;
}
通过选择权重最小的边,逐步构建最小生成树。
贪心算法是一种简单而高效的算法设计策略,适用于具有贪心选择性质和最优子结构的问题。它通过在每一步选择中采取当前状态下最优的选择,逐步构建解。虽然贪心算法不能保证总是得到全局最优解,但在很多实际问题中都能得到较好的近似解。在使用贪心算法时,需要仔细分析问题的性质,确保贪心策略的有效性。