DSA DSA In Depth
0%
lesson approximation-algorithmsfptasvertex-cover

Approximation Algorithms: 2-Approx Vertex Cover, Greedy ln N Set Cover, Metric TSP & Knapsack FPTAS

7 min read Module 6.4: Computational Geometry, Number Theory, Randomized & Approximation Algorithms Updated Oct 23, 2026
📄 Cheat Sheet
Introduction to Approximation Algorithms (Vertex Cover, TSP, Greedy Set Cover & PTAS)
Tim Roughgarden Lectures Introduction to Approximation Algorithms (Vertex Cover, TSP, Greedy Set Cover & PTAS) Watch on YouTube ↗

Tim Roughgarden establishes relative approximation ratios, LP-relaxation/combinatorial lower bounds, and polynomial-time approximation schemes for NP-hard problems.

0:00 / --:--

1. Formal Invariant — Lower-Bound Certificates & Value Scaling

1. Unweighted Vertex Cover 2-Approximation (Maximal Matching Lower Bound)

Greedily pick any uncovered edge $(u, v) \in E$, add both $u$ and $v$ to cover $C$, and delete all edges incident to $u$ or $v$. The chosen edges form a matching $M$ (pairwise vertex-disjoint). Since every valid Vertex Cover must pick at least $1$ endpoint from each edge in $M$, we have $OPT \ge |M|$, while our cover has size $|C| = 2|M| \le 2 \cdot OPT$!

2. Metric TSP 2-Approximation & Christofides-Serdyukov 1.5-Approximation

When edge weights obey the Triangle Inequality $w(u, v) \le w(u, x) + w(x, v)$:

  • Deleting one edge from the optimal tour $OPT$ gives a spanning tree, so $w(\text{MST}) \le OPT$. Doubling the MST and shortcutting repeated vertices via the Triangle Inequality gives a tour of cost $\le 2 \cdot w(\text{MST}) \le 2 \cdot OPT$.
  • Christofides (1976): Instead of doubling the whole MST, find a Minimum-Weight Perfect Matching $M^*$ on the set $O$ of odd-degree vertices of the MST. By parity, $w(M^*) \le \frac{1}{2} OPT$, so the Eulerian tour of $\text{MST} \cup M^*$ has cost $\le w(\text{MST}) + w(M^*) \le 1.5 \cdot OPT$!

3. 0-1 Knapsack FPTAS (Fully Polynomial-Time Approximation Scheme)

Let $v_{\max} = \max_i v_i$ and scale down every item's value by $K = \frac{\varepsilon \cdot v_{\max}}{N}$: $\tilde{v}_i = \lfloor v_i / K \rfloor$. Now $\max_i \tilde{v}_i = \lfloor N / \varepsilon \rfloor$, so the total scaled value of any subset is at most $N \cdot (N / \varepsilon) = N^2 / \varepsilon$. Running minimum-weight DP indexed by scaled value takes $O(N^3 / \varepsilon)$ time and loses at most $N \cdot K = \varepsilon \cdot v_{\max} \le \varepsilon \cdot OPT$!

2. The Inapproximability Spectrum

flowchart LR FPTAS["0-1 Knapsack
FPTAS: (1 - eps) in O(N^3 / eps)"] --> ConstApprox["Metric TSP (1.5-Approx)
Vertex Cover (2-Approx)
MAX-3SAT (7/8-Approx)"] ConstApprox --> LogApprox["Set Cover
Greedy H_n <= (ln n + 1)-Approx
(Tight under P != NP)"] LogApprox --> Inapprox["General TSP (No Triangle Ineq)
& Maximum Clique
Inapproximable within any poly factor!"]

3. Why General TSP (Without Triangle Inequality) Is Completely Inapproximable

Gap-Introducing Reduction from Hamiltonian Cycle

Suppose a polynomial-time algorithm could approximate General TSP within any factor $\rho \ge 1$. Given a graph $G = (V, E)$ for Hamiltonian Cycle, assign weight $1$ to every edge in $E$ and weight $\rho N + 1$ to every non-edge! If $G$ has a Hamiltonian cycle, $OPT = N$, so the $\rho$-approximation must return a tour of cost $\le \rho N$ (using only edges of weight $1$). If $G$ has no Hamiltonian cycle, any tour uses at least one non-edge and costs $\ge \rho N + N > \rho N$. Thus a $\rho$-approximation for General TSP would solve NP-Complete Hamiltonian Cycle in polynomial time!

4. Complexity & Approximation Ratio Matrix

Karger Randomized Edge Contraction, 3-SAT Gadget & Knapsack FPTAS
Pr ≥ 2/(n(n-1)) · (1-ε) FPTAS

Simulate Karger random multigraph edge contractions down to 2 supernodes, 3-SAT ≤_p Vertex Cover gadget graphs, and Knapsack FPTAS value-bucket scaling K = εP/n.

Step 1 / 7
Custom Input:
Timeline

Invariant & State

Since min-cut c ≤ 2E/V, a random contraction misses C* with probability ≥ 1 - 2/V. Telescoping over V-2 contractions gives Pr[Min-Cut] ≥ 2 / (V(V-1)).
{A, B, C} {D} 2 Supernodes Remaining ⇒ Cut Value = 2 Parallel Edges
Single Run Success
Pr ≥ 2 / (n(n - 1))
Karger-Stein Fast Cut
O(n² log³ n) w.h.p.
Surviving Cut
({A,B,C}, {D}) = 2 edges
[Step 1] Contracted non-cut edges (A,B) and (B,C); 2 supernodes {A,B,C} and {D} reveal min-cut of size 2.
Optimization Problem Approximation Algorithm Guarantee Ratio Time Complexity
Minimum Vertex Cover Maximal Matching / LP Pricing $2 \cdot OPT$ O(V + E)
Metric TSP (Triangle Inequality) Twice-Around-the-Tree (MST) $2 \cdot OPT$ O(E log V)
Metric TSP (Triangle Inequality) Christofides (MST + Odd MWPM) $1.5 \cdot OPT$ O(V^3)
Minimum Set Cover ($n$ elements) Greedy Min Cost per New Element $H_n \approx (\ln n + 1) \cdot OPT$ O(n * m)
0-1 Knapsack (FPTAS) Value Scaling $\tilde{v}_i = \lfloor v_i / K \rfloor$ $(1 - \varepsilon) \cdot OPT$ O(N^3 / eps)

5. Production-Grade C++20 Implementation (2-Approx Vertex Cover & Knapsack FPTAS)

#include <vector>
#include <cstdint>
#include <cmath>
#include <limits>
#include <algorithm>

// 1. Linear-Time 2-Approximation for Minimum Unweighted Vertex Cover via Maximal Matching
std::vector<int> vertex_cover_2approx(int n, const std::vector<std::pair<int, int>>& edges) {
    std::vector<bool> in_cover(n, false);
    std::vector<int> cover;
    for (const auto& [u, v] : edges) {
        if (!in_cover[u] && !in_cover[v]) {
            in_cover[u] = true;
            in_cover[v] = true;
            cover.push_back(u);
            cover.push_back(v);
        }
    }
    return cover; // Guaranteed: cover.size() <= 2 * OPT
}

// 2. 0-1 Knapsack FPTAS: Returns a subset value >= (1 - eps) * OPT in O(N^3 / eps) time
int64_t knapsack_fptas(const std::vector<int64_t>& weights,
                       const std::vector<int64_t>& values,
                       int64_t capacity,
                       double eps) {
    const int n = static_cast<int>(weights.size());
    int64_t v_max = 0;
    for (int i = 0; i < n; ++i) {
        if (weights[i] <= capacity) v_max = std::max(v_max, values[i]);
    }
    if (v_max == 0) return 0;

    double K = (eps * static_cast<double>(v_max)) / static_cast<double>(n);
    if (K < 1.0) K = 1.0; // Exact DP when K == 1

    std::vector<int> scaled_v(n, 0);
    int max_scaled_sum = 0;
    for (int i = 0; i < n; ++i) {
        if (weights[i] <= capacity) {
            scaled_v[i] = static_cast<int>(std::floor(values[i] / K));
            max_scaled_sum += scaled_v[i];
        }
    }

    constexpr int64_t INF = std::numeric_limits<int64_t>::max() / 4;
    // dp[v] = minimum weight required to achieve scaled value v
    std::vector<int64_t> dp(max_scaled_sum + 1, INF);
    std::vector<int64_t> real_val(max_scaled_sum + 1, 0);
    dp[0] = 0;

    int cur_max_v = 0;
    for (int i = 0; i < n; ++i) {
        if (weights[i] > capacity) continue;
        int sv = scaled_v[i];
        for (int v = cur_max_v; v >= 0; --v) {
            if (dp[v] + weights[i] <= capacity && dp[v] + weights[i] < dp[v + sv]) {
                dp[v + sv] = dp[v] + weights[i];
                real_val[v + sv] = std::max(real_val[v + sv], real_val[v] + values[i]);
            }
        }
        cur_max_v += sv;
    }

    int64_t best = 0;
    for (int v = 0; v <= max_scaled_sum; ++v) {
        if (dp[v] <= capacity) best = std::max(best, real_val[v]);
    }
    return best;
}

Production Pitfall — Why Greedy Max-Degree Vertex Cover Is NOT a 2-Approximation

It is tempting to approximate Minimum Vertex Cover by repeatedly picking the vertex of highest remaining degree (just like Greedy Set Cover). Shockingly, Greedy Max-Degree Vertex Cover is NOT a constant-factor approximation—on bipartite graphs constructed with degree-harmonic layers, it returns a cover of size $\Theta(\log N) \cdot OPT$! By contrast, the seemingly 'dumber' algorithm that picks both endpoints of any maximal matching is guaranteed to be within $2 \cdot OPT$.

Check yourself Question 1 of 4
Concept Check Core Invariant Check recall

Why does taking **both endpoints** of every edge in any Maximal Matching $M$ guarantee a 2-approximation ($|C| \le 2 \cdot OPT$) for Minimum Unweighted Vertex Cover?

⚡ Interactive Challenge · Staff Level

Algorithmic Design & Complexity Check

Analyze the invariant, asymptotic bound, and hardware behavior before selecting.

Why is **General TSP** (where edge weights are NOT required to obey the Triangle Inequality) impossible to approximate within *any* polynomial-time computable factor $\rho \ge 1$ unless $\mathbf{P} = \mathbf{NP}$?

Key Takeaways

Taking both endpoints of every edge in a Maximal Matching gives a linear-time 2-approximation for Minimum Unweighted Vertex Cover because OPT >= |M|.
Greedy Set Cover achieves an H_n <= (ln n + 1) approximation ratio, which is asymptotically optimal unless P = NP.
Metric TSP (with Triangle Inequality) admits a 2-approximation via MST doubling and a 1.5-approximation via Christofides' algorithm, whereas General TSP cannot be approximated within any polynomial factor.
The 0-1 Knapsack FPTAS scales item values by K = (eps * v_max) / N and runs minimum-weight-for-value DP in O(N^3 / eps) time to guarantee >= (1 - eps) * OPT.
Was this lesson helpful? Spot an issue or have a suggestion? Let us know on GitHub.
Report an issue