NC24953 [USACO 2008 Jan G]Cell Phone Network

题目链接

题目

题目描述

Farmer John has decided to give each of his cows a cell phone in hopes to encourage their social interaction. This, however, requires him to set up cell phone towers on his N (1 ≤ N ≤ 10,000) pastures (conveniently numbered 1..N) so they can all communicate.
Exactly N-1 pairs of pastures are adjacent, and for any two pastures A and B (1 ≤ A ≤ N; 1 ≤ B ≤ N; A ≠ B) there is a sequence of adjacent pastures such that A is the first pasture in the sequence and B is the last. Farmer John can only place cell phone towers in the pastures, and each tower has enough range to provide service to the pasture it is on and all pastures adjacent to the pasture with the cell tower.
Help him determine the minimum number of towers he must install to provide cell phone service to each pasture.

输入描述

  • Line 1: A single integer: N
  • Lines 2..N: Each line specifies a pair of adjacent pastures with two space-separated integers: A and B

输出描述

  • Line 1: A single integer indicating the minimum number of towers to install

示例1

输入

5
1 3
5 2
4 3
3 5

输出

2

说明

The towers can be placed at pastures 2 and 3 or pastures 3 and 5.

题解

知识点:树形dp。

题目要求用最少的点覆盖所有点,一个点能覆盖周围的点(最少支配集),所以有如下情况。

\(1\) 为根,设 \(dp[u][0/1/2]\) 为以 \(u\) 为根节点的子树且 \(u\) 不选被父节点覆盖/选/不选被孩子覆盖时的最小选点数。有转移方程:

\[\left \{ \begin{array}{l} dp[u][0] = \sum \min(dp[v_i][1],dp[v_i][2])\\ dp[u][1] = \sum \min(dp[v_i][0],dp[v_i][1],dp[v_i][2]) + 1\\ dp[u][2] = \sum \min(dp[v_i][1],dp[v_i][2]) + \min (delta,\max (dp[v_i][1]-dp[v_i][2],0)) \end{array} \right . \]

第一种情况,因为 \(u\) 没选被父节点覆盖,可以从子节点自己选了和子节点被子节点覆盖的情况转移。

第二种情况,\(u\) 选了那子节点所以都可以,最后加一。

第三种情况比较复杂,因为 \(u\) 要被子节点覆盖,而子节点只要有至少一个选了即可,因此先从子节点选了和没选但被子节点覆盖的情况转移最小值,即 \(\sum \max(dp[v_i][1],dp[v_i][2])\) 。当然,现在的最优解可能全是不选的,所以考虑所有子节点中选了减不选的差值最小的一组换掉,就有保证有选的情况,且是最优结果。特别地,如果本来就有至少一组是选了的情况,则 \(dp[v_i][1]-dp[v_i][2] <0\) ,这种情况 \(delta\) 应该为 \(0\) 表示不用改,所以 \(delta\) 最小不能小于 \(0\) ,最后 \(\min (delta,\max (dp[v_i][1]-dp[v_i][2],0))\)

时间复杂度 \(O(n)\)

空间复杂度 \(O(n)\)

代码

#include <bits/stdc++.h>

using namespace std;

vector<int> g[10007];
int dp[10007][3];

///0:u不选但被父节点覆盖,则1,2
///1:u选,则0,1,2
///2:u不选但被孩子覆盖,孩子只要有选一个就行,其他随意
///因此如果最小情况(即1,2最小值)已经有选了(即1更小)就不变,否则找到差值最小的一组

void dfs(int u, int fa) {
    int delta = 1e9;///选-不选的差值,没有孩子则不可能被孩子覆盖无穷大
    for (auto v : g[u]) {
        if (v == fa) continue;
        dfs(v, u);
        dp[u][0] += min(dp[v][1], dp[v][2]);
        dp[u][1] += min({ dp[v][0], dp[v][1], dp[v][2] });
        dp[u][2] += min(dp[v][1], dp[v][2]);
        delta = min(delta, max(dp[v][1] - dp[v][2], 0));///如果存在小于0的说明肯定有选的那就改为0不变,否则找到差值最小的一组改掉
    }
    dp[u][1]++;
    dp[u][2] += delta;
}

int main() {
    std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    int n;
    cin >> n;
    for (int i = 1;i < n;i++) {
        int u, v;
        cin >> u >> v;
        g[u].push_back(v);
        g[v].push_back(u);
    }
    dfs(1, 0);
    cout << min(dp[1][1], dp[1][2]) << '\n';///根节点不可能有0
    return 0;
}
posted @ 2022-08-23 19:00  空白菌  阅读(30)  评论(0编辑  收藏  举报