// Universe U, collection S = [S0, S1, ...] subsets of U. Optional weights w[i] >= 0.
function greedySetCover(U, S, w = []) {
const n = U.length; const Uset = new Set(U);
const chosen = []; const covered = new Set();
const weights = w.length ? w : S.map(() => 1);
while (covered.size < n) {
let best = -1, bestScore = Infinity, bestGain = 1;
for (let i = 0; i < S.length; i++) {
const gain = S[i].reduce((c, x) => c + (!covered.has(x) ? 1 : 0), 0);
if (gain === 0) continue;
const score = weights[i] / gain; // cost-effectiveness
if (score < bestScore) { bestScore = score; bestGain = gain; best = i; }
}
if (best === -1) throw new Error('Uncoverable element: instance infeasible');
chosen.push(best);
for (const x of S[best]) covered.add(x);
}
return chosen; // indices of chosen sets
}
- Guarantee: H(d)-approx where d is max set size; ≤ H(n) ≤ ln n + 1.
- Idea: pick set with best cost-per-newly-covered element at each step.
- Weighted case: same greedy by cost-effectiveness achieves ln n approximation.