Vertex Cover#265

Vertex Cover (Editorial)

Підвісимо дерево за першу (будь-яку) вершину.

Позначимо G[v] - найменшу кількість вершин якими можемо покрити піддерево з коренем v, якщо вершину v берем NG[v] - найменшу кількість вершин якими можемо покрити піддерево з коренем v, якщо вершину v не берем.

Якщо ми не беремо вершину v, то всіх її дітей маємо взяти обов'язково (бо ребра між v і дітьми лишаться непокриті) отже NG[v] = sum (G[to]). to - всі сини вершини v.

Якщо ми беремо вершину V, то дітей можемо як взяти так і не взяти. Дивимось для кожного сина, чи його оптимальніше взяти чи ні. NG[v] = sum (min(G[to], NG[to])). to - всі сини вершини v.

Отже просто запустимо пошук в глибину (dfs) і під час обходу будемо рахувати значення G[v], NG[v].

відповідь буде min(G[1], NG[1]).


#include <bits/stdc++.h>

using namespace std;

#define DIM 100007

long long G[DIM],NG[DIM];
vector<int> A[DIM];
long long n;

void dfs(int v, int parent) {
    G[v] = 1; //якщо вершину берем - то рахуємо її
    NG[v] = 0; //якщо не берем, то не рахуємо
    for(auto to : A[v]) //перевіряємо всіх дітей
        if(to != parent) {
            dfs(to, v); //запускаємо пошук в глибину для сина
            G[v] += min( G[to], NG[to] ); //якщо вершина V в покритті, то дітей можна як брати так і не брати
            NG[v] += G[to]; //ящо вершина V не в покритті, то синів треба обов'язково взяти (щоб ребра між V і синами були покриті
        }
}

int main()
{
    cin >> n;
    for(int i = 1; i < n; i++) {
        long long v1,v2;
        cin >> v1 >> v2;
        A[v1].push_back(v2);
        A[v2].push_back(v1);
    }

    dfs(1,-1); //приймемо за корінь першу вершину
    cout<<min(G[1], NG[1])<<endl; //мінімум з варіанті коли корінь берем і не берем

    return 0;
}