P3629 [APIO2010] 树的直径 DFS + 树形 DP

159 篇文章 1 订阅
58 篇文章 0 订阅
该博客探讨了在K条边限制下,如何优化树状结构的巡逻路径。当K=0时,路径长度为2*(n-1)。随着K增加到1和2,新增边会改变树的直径,目标是最大化或最小化树的直径。博主通过决策包容性证明和树形DP解决K=2时的问题,展示了求解树的直径的方法,并给出了C++代码实现。
摘要由CSDN通过智能技术生成
题意

传送门 P3629 [APIO2010]巡逻

题解

K = 0 K=0 K=0,遍历整棵树,每条边必然被递归依次,回溯一次,故路线总长度为 2 × ( n − 1 ) 2\times (n-1) 2×(n1)

K = 1 K=1 K=1,由于新增的边必须正好经过一次,那么由于新增边而构成的环需要遍历一次,则环上的树边经过次数都减少一次,那么目标为最大化环上的树边,那么树的直径即可减小的最大权值。

K = 2 K=2 K=2,新增的边又构成一个环,若两条新增道路构成的环不重叠,那对于新增的环上的树边贡献依然是 − 1 -1 1,若存在重叠,由于环上的边需遍历一次,故贡献为 1 1 1。目标是使总距离最小,那么对于 K = 1 K=1 K=1 情况的环上树边的边权取负,再次计算树的直径,即此时可减小的最大权值。

可以用决策包容性证明迭代求树的直径可以求解 K ≥ 2 K\geq 2 K2 情况下的答案。

处理环上边权取负时,使用 D F S DFS DFS 求解树的直径,记录直径上的边;但 D F S DFS DFS 无法处理边权为负的情况,于是使用树形 D P DP DP 求解 K = 2 K=2 K=2 情况下树的直径。

#include <bits/stdc++.h>
using namespace std;
#define maxn 100005
int N, K, d[maxn], pree[maxn];
int head[maxn << 1], nxt[maxn << 1], to[maxn << 1], cost[maxn << 1], tot = 1;

void add(int x, int y, int z)
{
    to[++tot] = y, cost[tot] = z, nxt[tot] = head[x], head[x] = tot;
}

void dfs(int x, int f, int &t)
{
    if (d[x] > d[t])
        t = x;
    for (int i = head[x]; i; i = nxt[i])
    {
        int y = to[i], z = cost[i];
        if (y != f)
            d[y] = d[x] + z, pree[y] = i, dfs(y, x, t);
    }
}

void dp(int x, int f, int &t)
{
    for (int i = head[x]; i; i = nxt[i])
    {
        int y = to[i], z = cost[i];
        if (y != f)
        {
            dp(y, x, t);
            t = max(t, d[x] + d[y] + z);
            d[x] = max(d[x], d[y] + z);
        }
    }
}

int main()
{
    scanf("%d%d", &N, &K);
    for (int i = 1; i < N; ++i)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a, b, 1);
        add(b, a, 1);
    }
    int s = 1, t = 0, res = (N - 1) << 1;
    dfs(s, 0, t);
    s = t, t = 0, d[s] = pree[s] = 0;
    dfs(s, 0, t);
    res -= d[t] - 1;
    if (K == 2)
    {
        for (int i = pree[t]; i; i = pree[to[i ^ 1]])
            cost[i] = cost[i ^ 1] = -1;
        memset(d, 0, sizeof(d));
        t = 0;
        dp(1, 0, t);
        res -= t - 1;
    }
    printf("%d\n", res);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值