シリーズ: Algorithms
cpp
156 行
· 更新日 2026-07-24
vertex_cover.cpp
Algorithms/Advanced/vertex_cover.cpp
// vertex_cover.cpp
// -----------------------------------------------------------------
// Vertex Cover(頂點覆蓋):
// 1. 精確解:k-分支限界(FPT,O(2^k · poly))+ 求最小覆蓋
// 2. 2-近似:極大匹配的兩端點(保證 <= 2·OPT)
// 3. 度數啟發式(貪婪挑最大度)——無保證的對照組
//
// Compile: g++ -std=c++17 -O2 -Wall -Wextra -o vertex_cover vertex_cover.cpp
// -----------------------------------------------------------------
#include <iostream>
#include <vector>
#include <algorithm>
#include <set>
using namespace std;
using Edges = vector<pair<int,int>>;
// ---------- 1. 精確:分支限界 ----------
// 觀察:任一條未覆蓋邊 (u,v),答案必含 u 或 v -> 兩分支,深度 <= k
class VertexCoverExact {
public:
long long nodes = 0;
// 是否存在大小 <= k 的覆蓋;cover 傳回實際選點
bool solveK(const Edges& edges, int n, int k, vector<int>& cover) {
nodes = 0;
vector<bool> chosen(n, false);
bool ok = dfs(edges, chosen, k);
cover.clear();
for (int i = 0; i < n; ++i)
if (chosen[i]) cover.push_back(i);
return ok;
}
// 最小覆蓋:k 從 0 遞增
vector<int> minimumCover(const Edges& edges, int n) {
vector<int> cover;
for (int k = 0; k <= n; ++k)
if (solveK(edges, n, k, cover)) return cover;
return cover;
}
private:
bool dfs(const Edges& edges, vector<bool>& chosen, int k) {
++nodes;
// 找第一條未覆蓋的邊
const pair<int,int>* uncovered = nullptr;
for (const auto& e : edges)
if (!chosen[e.first] && !chosen[e.second]) { uncovered = &e; break; }
if (!uncovered) return true; // 全覆蓋
if (k == 0) return false; // 配額用盡
auto [u, v] = *uncovered;
chosen[u] = true; // 分支 1:選 u
if (dfs(edges, chosen, k - 1)) return true;
chosen[u] = false;
chosen[v] = true; // 分支 2:選 v
if (dfs(edges, chosen, k - 1)) return true;
chosen[v] = false;
return false;
}
};
// ---------- 2. 2-近似:極大匹配 ----------
vector<int> vertexCover2Approx(const Edges& edges, int n) {
vector<bool> in(n, false);
vector<int> cover;
for (auto [u, v] : edges) {
if (in[u] || in[v]) continue;
in[u] = in[v] = true; // 兩端都拿
cover.push_back(u);
cover.push_back(v);
}
return cover;
}
// ---------- 3. 貪婪最大度(教學對照:無近似保證!) ----------
vector<int> vertexCoverGreedyDegree(Edges edges, int n) {
vector<int> cover;
while (true) {
vector<int> deg(n, 0);
for (auto [u, v] : edges) { ++deg[u]; ++deg[v]; }
int best = max_element(deg.begin(), deg.end()) - deg.begin();
if (deg[best] == 0) break;
cover.push_back(best);
Edges rest;
for (auto& e : edges)
if (e.first != best && e.second != best) rest.push_back(e);
edges = std::move(rest);
}
return cover;
}
static bool verifyCover(const Edges& edges, const vector<int>& cover) {
set<int> s(cover.begin(), cover.end());
for (auto [u, v] : edges)
if (!s.count(u) && !s.count(v)) return false;
return true;
}
static void show(const string& tag, const vector<int>& cover, const Edges& e) {
cout << tag << " size=" << cover.size() << " {";
for (size_t i = 0; i < cover.size(); ++i)
cout << cover[i] << (i + 1 < cover.size() ? "," : "");
cout << "} valid=" << (verifyCover(e, cover) ? "yes" : "NO!") << "\n";
}
int main() {
// ---- 範例 1:星星圖(貪婪==最優,近似=2x) ----
cout << "=== Example 1: star graph K1,5 ===\n";
Edges star = {{0,1},{0,2},{0,3},{0,4},{0,5}};
VertexCoverExact ex;
show("exact ", ex.minimumCover(star, 6), star);
show("2-apx ", vertexCover2Approx(star, 6), star);
show("greedy ", vertexCoverGreedyDegree(star, 6), star);
cout << "\n";
// ---- 範例 2:路徑 P6(近似的表現) ----
cout << "=== Example 2: path P6 ===\n";
Edges path = {{0,1},{1,2},{2,3},{3,4},{4,5}};
show("exact ", ex.minimumCover(path, 6), path);
show("2-apx ", vertexCover2Approx(path, 6), path);
cout << "\n";
// ---- 範例 3:Petersen 圖(OPT=6) ----
cout << "=== Example 3: Petersen graph ===\n";
Edges pet = {
{0,1},{1,2},{2,3},{3,4},{4,0},
{5,7},{7,9},{9,6},{6,8},{8,5},
{0,5},{1,6},{2,7},{3,8},{4,9}};
auto opt = ex.minimumCover(pet, 10);
show("exact ", opt, pet);
cout << " (branching nodes = " << ex.nodes << ")\n";
show("2-apx ", vertexCover2Approx(pet, 10), pet);
show("greedy ", vertexCoverGreedyDegree(pet, 10), pet);
cout << "\n";
// ---- 範例 4:FPT 的意義——n 大但 k 小 ----
cout << "=== Example 4: FPT — big n, small k ===\n";
// 100 個頂點的稀疏圖:一條長鏈 + 幾條額外邊
Edges big;
for (int i = 0; i < 20; ++i) big.push_back({i, i + 1});
big.push_back({30, 40});
big.push_back({50, 60});
big.push_back({70, 80});
vector<int> cov;
for (int k = 1; k <= 14; ++k) {
if (ex.solveK(big, 100, k, cov)) {
cout << "n=100, |E|=" << big.size()
<< " -> minimum cover k=" << k
<< " (nodes=" << ex.nodes << ")\n";
break;
}
}
cout << "Search depth bounded by k, not n -> FPT: O(2^k * poly(n))\n";
return 0;
}
関連記事
Algorithms
java
更新日 2026-03-02
#include <iostream>.java
#include <iostream>.java — java source code from the Algorithms learning materials (Algorithms/#include <iostream>.java).
記事を読む →
Algorithms
cpp
更新日 2026-04-07
748.cpp
748.cpp — cpp source code from the Algorithms learning materials (Algorithms/748.cpp).
記事を読む →
Algorithms
cpp
更新日 2026-04-07
827.cpp
827.cpp — cpp source code from the Algorithms learning materials (Algorithms/827.cpp).
記事を読む →
Algorithms
cpp
更新日 2026-04-07
827_best_greedy.cpp
827_best_greedy.cpp — cpp source code from the Algorithms learning materials (Algorithms/827_best_greedy.cpp).
記事を読む →
Algorithms
cpp
更新日 2026-04-07
8402.cpp
8402.cpp — cpp source code from the Algorithms learning materials (Algorithms/8402.cpp).
記事を読む →
Algorithms
cpp
更新日 2026-04-07
860.cpp
860.cpp — cpp source code from the Algorithms learning materials (Algorithms/860.cpp).
記事を読む →