算法提高课第一章树形dp

1072. 树的最长路径 - AcWing题库

给定一棵树,树中包含 n n n 个结点(编号 1 − n 1 -n 1n)和 n − 1 n−1 n1条无向边,每条边都有一个权值。

现在请你找到树中的一条最长路径。

换句话说,要找到一条路径,使得使得路径两端的点的距离最远。

注意:路径中可以只包含一个点。

也就是求树的直径,对于无边权树来说,可以先求出距离任意点最远点,然后求出最远点的最长路径,即为树的直径。有边权的一般情况,可以求出所有挂在高点上的路径,最长的就是直径。
在这里插入图片描述

输入格式
第一行包含整数 n n n

接下来 n−1n−1n-1 行,每行包含三个整数 a i , b i , c i a_i,b_i,c_i ai,bi,ci,表示点 a i 和 b i a_i和b_i aibi之间存在一条权值为 c i c_i ci的边。

输出格式

输出一个整数,表示树的最长路径的长度。

数据范围

1 ≤ n ≤ 10000 1≤n≤10000 1n10000
1 ≤ a i , b i ≤ n 1≤a_i,b_i≤n 1ai,bin
1 ≤ c i ≤ 1 0 5 1≤c_i≤10^5 1ci105

输入样例:

6
5 1 6
1 4 5
6 3 9
2 6 8
6 1 7 

输出样例:

22 
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
int n;
const int N = 100010;
const int M = N;
int h[N], e[M], ne[M], idx, w[M];
int ans = 0;
void add(int a, int b, int c)  // 添加一条边a->b,边权为c
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ; 
}
int dfs(int u, int fa)
{
    int dist = 0;//表示从当前点往下走的最大长度
    int d1 = 0, d2 = 0;
    for(int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if(j == fa) continue;
        int d = dfs(j, u) + w[i];
        dist = max(dist, d);

        if(d >= d1) d2 = d1, d1 = d;
        else if(d > d2) d2 = d;
    }
    ans = max(ans, d1 + d2);
    return dist;
}
int main()
{
    memset(h, -1, sizeof h);
    cin >> n;
    for(int i = 1; i <= n - 1; i ++ )
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);
        add(b, a, c);
    }
    dfs(1, -1);
    cout << ans << endl;
}

1073. 树的中心 - AcWing题库

给定一棵树,树中包含 n n n 个结点(编号 1 − n 1-n 1n)和 n − 1 n−1 n1条无向边,每条边都有一个权值。

请你在树中找到一个点,使得该点到树中其他结点的最远距离最近。

求出所有点的向下走的最长路径,然后求出向上走的最长路径,也就是点1不回到点2的最长路径
因此要求出d1、d2、up和p1(去往d1的节点,防止1回到2)这样就可以找到树的中心
在这里插入图片描述
输入格式

第一行包含整数 n n n

接下来 n − 1 n−1 n1 行,每行包含三个整数 a i , b i , c i a_i,b_i,c_i ai,bi,ci,表示点 a i 和 b i a_i和b_i aibi之间存在一条权值为 c i c_i ci的边。

输出格式

输出一个整数,表示所求点到树中其他结点的最远距离。

数据范围

1 ≤ n ≤ 10000 1≤n≤10000 1n10000
1 ≤ a i , b i ≤ n 1≤a_i,b_i≤n 1ai,bin
1 ≤ c i ≤ 1 0 5 1≤c_i≤10^5 1ci105

输入样例:

5 
2 1 1 
3 2 1 
4 3 1 
5 1 1 

输出样例:

2 
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 2 * 10000;
const int M = N;
const int inf = 1e9;
int n;
int h[N], e[M], w[M], ne[M], idx;
int d1[N], d2[N], up[N], p1[N], p2[N];

void add(int a, int b, int c)  // 添加一条边a->b,边权为c
{
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}
int dfs_d(int u, int fa)
{
    d1[u] = d2[u] = -inf;
    for(int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if(j == fa) continue;
        int d = dfs_d(j, u) + w[i];
 
        if(d >= d1[u]) 
        {
            d2[u] = d1[u], d1[u] = d;
            p2[u] = p1[u], p1[u] = j;
        }
        else if(d > d2[u]) d2[u] = d, p2[u] = j;
    }
    if(d1[u] == -inf) d1[u] = d2[u] = 0;
    return d1[u];
}
void dfs_u(int u, int fa)
{
    for(int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if(j == fa) continue;
        if(p1[u] == j) up[j] = max(up[u], d2[u]) + w[i];
        else up[j] = max(up[u], d1[u]) + w[i];
        dfs_u(j, u);
    }
}
int main()
{
    memset(h, -1, sizeof h);
    cin >> n;
    for(int i = 1; i <= n; i ++ )
    {
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c);add(b, a, c);
    }
    dfs_d(1, -1);
    dfs_u(1, -1);
    int ans = inf;
    for(int i = 1; i <= n; i ++ ) ans = min(ans, max(d1[i], up[i]));
    cout << ans;
}

1075. 数字转换 - AcWing题库

父亲节点只有一个,就是他的约数和

  1. 使用筛法求出所有点的父亲节点
  2. 连接父亲节点子节点,并判断根节点(森林)
  3. 求出最长路径
#include <iostream>
#include <cstring>
#include <algorithm>

using namespace std;
const int N = 55000;
const int M = 2 * N; 
int f[N];
int h[N], e[M], w[M], ne[M], idx;
bool st[N];
int n;
int ans = 0;
void add(int a, int b)  // 添加一条边a->b
{
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

int dfs(int u, int fa)
{
    int d1 = 0, d2 = 0;
    for(int i = h[u]; i != -1; i = ne[i])
    {
        int j = e[i];
        if(j == fa) continue;

        int d = dfs(j, u) + 1;
        if(d >= d1) d2 = d1, d1 = d;
        else if(d >= d2) d2 = d;
    }
    ans = max(ans, d1 + d2);
    return d1;
}
int main()
{
    memset(h, -1, sizeof h);
    cin >> n;
    for(int i = 1; i <= n / 2; i++ )
    for(int j = 2; j <= n / i; j ++ )
    {
        f[i * j] += i;// i 是 i * j 的约数
    }
    for(int i = 1; i <= n; i ++ )
    {
        if(f[i] < i && f[i] > 0)
        {
            add(f[i], i);
            st[i] = true; //找到父亲节点。
        }
    }
    for(int i = 1; i <= n; i ++ )
    if(!st[i]) dfs(i, -1);//寻找最长路径
    cout << ans;
}

1074. 二叉苹果树 - AcWing题库

323. 战略游戏 - AcWing题库

1077. 皇宫看守 - AcWing题库

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值