洛谷 P2279 树上k距离覆盖问题【贪心 + 思维】

传送门
题意: 给定一颗树, 若在i点安放了一个消防站, 那么在树上距离它不超过2的所有点都可以被照顾到, 那么要让整棵树都要照顾到最少需要安放多少个消防站,

思路: 贪心, 就是找最深没被覆盖到的点, 并在它的祖父处设一个消防站. 考虑到这个点的所有子孙后代都已经被覆盖了, 因此这时覆盖祖父能盖到更多额外的点, 并保证结果不会更差.

我们在输入时只要预处理出深度(边输入边处理)并排序, 碰到已覆盖就跳过, 未覆盖就在祖父处设消防站, ans++。

问题在于怎样才能判断这个点覆盖到了没有. 对于儿子或孙子覆盖他, 可以在在儿子处设站时就标记它;而对于父亲和祖父覆盖他, 可以用儿子对父亲的映射f来解决; 问题在于兄弟. 其实, 可以用d数组维护“离i最近的消防站到i的距离”,当o[父亲]==1时, 就能确定它是否被覆盖
具体细节请看代码实现:

AC Code

int fa[maxn], a[maxn], d[maxn], dep[maxn];
bool cmp(int x, int y) {
    return dep[x] > dep[y];
}
void solve() {
    int n; scanf("%d", &n);
    a[1] = dep[1] = 1; d[1] = d[0] = n;
    for (int i = 2 ; i <= n ; i ++) {
        int x; scanf("%d", &x);
        d[i] = n; fa[i] = x; a[i] = i;
        dep[i] = dep[x] + 1;
    }
    sort(a+1, a+1+n, cmp); int ans = 0;
    for (int i = 1 ; i <= n ; i ++) {
        int u = a[i], f1 = fa[u], f2 = fa[fa[u]];
        d[u] = min(d[u], min(d[f1]+1, d[f2]+2));

        if (d[u] > 2) {
            d[f2] = 0; ++ ans;
            d[fa[f2]] = min(d[fa[f2]], 1);
            d[fa[fa[f2]]] = min(d[fa[fa[f2]]], 2);
        }
    }
    printf("%d\n", ans);
}

并且, 这种方法的普适性很强, 可以解决半径为k的最小覆盖问题. 而且不用存图. 只需要把维护“父亲和爷爷”改成维护“上位k位祖先”即可, 复杂度O(N*K), 常数也很小.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值