我是靠谱客的博主 美好戒指,最近开发中收集的这篇文章主要介绍HDU 6613 Squrirrel【树形DP + 换根】,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

题意:给你一棵树,你可以把某条边的边权变为0,然后选一个点,使得所有点到这个点的距离和最小。

思路:之前做过一道题没有变为0的操作,那道题直接求直径就可以,这道题比较麻烦,网上看了半天,,,

首先我们先dfs一遍求出来点1的答案,设dp[i][0][1]为以i为根的最长路径的最小长度,考虑对于相邻两个点u, v , 我们可能会删除u, v 之间的边或者之前就将边删除了然后加上w[u][v],所以dp[u][0][1] = min(dp[v][0][0], max(dp[v][0][1], dp[v][1][0]) + w[u][v])。

接着我们考虑换根的影响,用f[i][2] 记录一下当前节点与父节点间在哪删除边的答案,f[i][0]是来源于i的父节点方向最长距离和父节点的子树的最长距离取max再加上i和父节点的边权。如果次长距离是点i的话我们就需要维护第三长的距离。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1|1
const int maxn = 2e5 + 10;
const int inf = 0x3f3f3f3f;
vector<pair<int, int> > e[maxn]; //邻接点,权值
pair<int, int> dp[maxn][3][2]; //dp[i][j][k]:i:以i为根 ,j:0表示最长  1表示次长 2表示第3长 ,k:1带修改 0不带修改
int f[maxn][2], d[maxn]; //f[i][2]表示父节点换根的最大路径长度(修改/不修改)
int ans, id;

void init(int n)
{
    ans = inf, id = n;
    for(int i = 1; i <= n; ++i)
    {
        e[i].clear();
        memset(dp[i], 0, sizeof(dp[i])); //first是答案,second是结点
        memset(f[i], 0, sizeof(f[i]));
        d[i] = 0;
    }
}
void dfs(int u, int fa)
{
    for(auto x : e[u])
    {
        int v = x.first;
        if(v == fa) continue;
        dfs(v, u);
        d[v] = x.second;
        if(dp[v][0][0].first + d[v] >= dp[u][0][0].first) //维护三个最大
        {
            dp[u][2][0] = dp[u][1][0];
            dp[u][1][0] = dp[u][0][0];
            dp[u][0][0] = make_pair(dp[v][0][0].first + d[v], v);
        }
        else if(dp[v][0][0].first + d[v] >= dp[u][1][0].first)
        {
            dp[u][2][0] = dp[u][1][0];
            dp[u][1][0] = make_pair(dp[v][0][0].first + d[v], v);
        }
        else if(dp[v][0][0].first + d[v] >= dp[u][2][0].first)
            dp[u][2][0] = make_pair(dp[v][0][0].first + d[v], v);
    }
    int v = dp[u][0][0].second; //更新带修改的值
    dp[u][0][1].first = min(dp[v][0][0].first, max(dp[v][0][1].first, dp[v][1][0].first) + d[v]);
    v = dp[u][1][0].second;
    dp[u][1][1].first = min(dp[u][0][0].first, max(dp[v][0][1].first, dp[v][1][0].first) + d[v]);
}
void dfs2(int u, int fa) //换根
{
    int tmp1 = max(dp[u][0][1].first, dp[u][1][0].first), tmp2 = max(dp[u][0][0].first, f[u][1]);
    int res = min(max(tmp1, f[u][0]), tmp2);
    if(res < ans)
        ans = res, id = u;
    else if(res == ans)
        id = min(id, u);
    for(auto x : e[u])
    {
        int v = x.first;
        if(v == fa) continue;
        if(v == dp[u][0][0].second)
        {
            f[v][0] = max(dp[u][1][0].first, f[u][0]) + x.second;
            f[v][1] = min(max(dp[u][1][0].first, f[u][0]), min(max(f[u][0], max(dp[u][1][1].first, dp[u][2][0].first)), max(dp[u][1][0].first, f[u][1])) + x.second);
        }
        else
        {
            f[v][0] = max(dp[u][0][0].first, f[u][0]) + x.second;
            if(dp[u][1][0].second == v) //如果修改同一个地方
                f[v][1] = min(max(dp[u][0][0].first, f[u][0]), min(max(f[u][0], max(dp[u][0][1].first, dp[u][2][0].first)), max(dp[u][0][0].first, f[u][1])) + x.second);
            else
                f[v][1] = min(max(dp[u][0][0].first, f[u][0]), min(max(f[u][0], max(dp[u][0][1].first, dp[u][1][0].first)), max(dp[u][0][0].first, f[u][1])) + x.second);
        }
        dfs2(v, u);
    }
}

int main()
{
    int t;
    scanf("%d", &t);
    while(t--)
    {
        int n;
        scanf("%d", &n);
        init(n);
        for(int i = 1; i < n; ++i)
        {
            int u, v, w;
            scanf("%d%d%d", &u, &v, &w);
            e[u].push_back({v, w});
            e[v].push_back({u, w});
        }
        dfs(1, -1);
        ans = max(dp[1][0][1].first, dp[1][1][0].first);
        dfs2(1, -1);
        printf("%d %dn", id, ans);
    }
    return 0;
}

 

最后

以上就是美好戒指为你收集整理的HDU 6613 Squrirrel【树形DP + 换根】的全部内容,希望文章能够帮你解决HDU 6613 Squrirrel【树形DP + 换根】所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(34)

评论列表共有 0 条评论

立即
投稿
返回
顶部