动态规划-树形DP-习题

文章目录

  • AcWing 1072. 树的最长路径
    • 题目
    • 题解
    • 代码
  • AcWing 1073. 树的中心
    • 题目
    • 题解
    • 代码
  • AcWing 1075. 数字转换
    • 题目
    • 题解
    • 代码
  • AcWing 1074. 二叉苹果树
    • 题目
    • 题解
    • 代码
  • AcWing 323. 战略游戏
    • 题目
    • 题解
    • 代码
  • AcWing 1077. 皇宫看守
    • 题目
    • 题解
    • 代码

AcWing 1072. 树的最长路径

题目

传送门:AcWing 1072. 树的最长路径
动态规划-树形DP-习题_第1张图片
输入样例:

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

输出样例:

22

题解

动态规划-树形DP-习题_第2张图片

代码

// https://www.acwing.com/activity/content/code/content/1215928/
#include 
#include 
#include 

using namespace std;

const int N = 10010, M = N * 2;

int n;
int h[N], e[M], w[M], ne[M], idx;
int ans;

void add(int a, int b, int c) {
     
    e[++idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx;
}

int dfs(int u, int father) {
     
    int dist = 0; // 表示从当前点往下走的最大长度
    int d1 = 0, d2 = 0;

    for (int i = h[u]; i; i = ne[i]) {
     
        int j = e[i];
        if (j == father)
            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() {
     
    cin >> n;
    for (int i = 0; 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;

    return 0;
}

AcWing 1073. 树的中心

题目

传送门:AcWing 1073. 树的中心
动态规划-树形DP-习题_第3张图片
输入样例:

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

输出样例:

2

题解

动态规划-树形DP-习题_第4张图片

代码

// https://www.acwing.com/activity/content/code/content/1219105/
#include 
#include 
#include 
using namespace std;
const int N = 1e5 + 5, INF = 1e9;
int d1[N], d2[N], up[N];
int p1[N], p2[N];
int h[N], ne[2 * N], e[2 * N], idx, w[N];
int n;
void add(int a, int b, int c) {
     
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
}
int dfs_down(int u, int father) {
      //返回u的最长向下路径
    d1[u] = d2[u] = -INF;
    for (int i = h[u]; ~i; i = ne[i]) {
     
        int j = e[i];
        if (j == father)
            continue;
        int dist = dfs_down(j, u) + w[i];
        if (dist > d1[u]) {
      //更新一下最长和第二长的路径,并记录下从该路径是从哪一个点下去的
            d2[u] = d1[u], d1[u] = dist;
            p2[u] = p1[u], p1[u] = j;
        } else if (dist > d2[u]) {
     
            d2[u] = dist, p2[u] = j;
        }
    }
    if (d1[u] == -INF)
        d1[u] = d2[u] = 0; //如果没有改变过该点的距离,就证明这个点是叶节点
    return d1[u];
}
void dfs_up(int u, int father) {
      //用父节点更新一下子节点向上的最长路径
    for (int i = h[u]; ~i; i = ne[i]) {
     
        int j = e[i];
        if (j == father)
            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_up(j, u);
    }
}
int main() {
     
    cin >> n;
    memset(h, -1, sizeof h);
    for (int i = 0; i < n - 1; i++) {
     
        int a, b, c;
        cin >> a >> b >> c;
        add(a, b, c), add(b, a, c);
    }
    dfs_down(1, -1);
    dfs_up(1, -1);
    int res = INF;
    for (int i = 1; i <= n; i++)
        res = min(res, max(d1[i], up[i]));
    cout << res;
}

AcWing 1075. 数字转换

题目

传送门:AcWing 1075. 数字转换
动态规划-树形DP-习题_第5张图片

题解

动态规划-树形DP-习题_第6张图片

代码

// https://www.acwing.com/activity/content/code/content/1219611/
#include 
#include 
#include 

using namespace std;

const int N = 50010, M = N;

int n;
int h[N], e[M], w[M], ne[M], idx;
int sum[N];
bool st[N];
int ans;

void add(int a, int b) {
     
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int dfs(int u) {
     
    st[u] = true;

    int dist = 0;
    int d1 = 0, d2 = 0;
    for (int i = h[u]; ~i; i = ne[i]) {
     
        int j = e[i];
        if (!st[j]) {
     
            int d = dfs(j);
            dist = max(dist, d);
            if (d >= d1)
                d2 = d1, d1 = d;
            else if (d > d2)
                d2 = d;
        }
    }

    ans = max(ans, d1 + d2);

    return dist + 1;
}

int main() {
     
    cin >> n;
    memset(h, -1, sizeof h);

    for (int i = 1; i <= n; i++)
        for (int j = 2; j <= n / i; j++)
            sum[i * j] += i;

    for (int i = 2; i <= n; i++)
        if (sum[i] < i)
            add(sum[i], i);

    for (int i = 1; i <= n; i++)
        if (!st[i]) {
     
            dfs(i); // 只需要dfs(1)即可
            break;
        }

    cout << ans << endl;

    return 0;
}

AcWing 1074. 二叉苹果树

题目

传送门:AcWing 1074. 二叉苹果树
动态规划-树形DP-习题_第7张图片
输入样例:

5 2
1 3 1
1 4 10
2 3 20
3 5 20

输出样例:

21

题解

动态规划-树形DP-习题_第8张图片

代码

// https://www.acwing.com/activity/content/problem/content/1305/1/
#include 
#include 
#include 

using namespace std;

const int N = 110, M = N * 2;

int n, m;
int h[N], e[M], w[M], ne[M], idx;
int f[N][N];

void add(int a, int b, int c)
{
     
    e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u, int father)
{
     
    for (int i = h[u]; ~i; i = ne[i])
    {
     
        if (e[i] == father) continue;
        dfs(e[i], u);
        for (int j = m; j; j -- )
            for (int k = 0; k + 1 <= j; k ++ )
                f[u][j] = max(f[u][j], f[u][j - k - 1] + f[e[i]][k] + w[i]);
    }
}

int main()
{
     
    cin >> n >> m;
    memset(h, -1, sizeof h);
    for (int i = 0; i < n - 1; i ++ )
    {
     
        int a, b, c;
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c), add(b, a, c);
    }

    dfs(1, -1);

    printf("%d\n", f[1][m]);

    return 0;
}

AcWing 323. 战略游戏

题目

传送门:AcWing 323. 战略游戏
动态规划-树形DP-习题_第9张图片
输入样例:

4
0:(1) 1
1:(2) 2 3
2:(0)
3:(0)
5
3:(3) 1 4 2
1:(1) 0
2:(0)
0:(0)
4:(0)

输出样例:

1
2

题解

动态规划-树形DP-习题_第10张图片

代码

#include 
#include 
#include 

using namespace std;

const int N = 1510;

int n;
int h[N], e[N], ne[N], idx;
int f[N][2];
bool st[N];

void add(int a, int b)
{
     
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u)
{
     
    f[u][0] = 0, f[u][1] = 1;
    for (int i = h[u]; ~i; i = ne[i])
    {
     
        int j = e[i];
        dfs(j);
        f[u][0] += f[j][1];
        f[u][1] += min(f[j][0], f[j][1]);
    }
}

int main()
{
     
    while (cin >> n)
    {
     
        memset(h, -1, sizeof h);
        idx = 0;

        memset(st, 0, sizeof st);
        for (int i = 0; i < n; i ++ )
        {
     
            int id, cnt;
            scanf("%d:(%d)", &id, &cnt);
            while (cnt -- )
            {
     
                int ver;
                cin >> ver;
                add(id, ver);
                st[ver] = true;
            }
        }

        int root = 0;
        while (st[root]) root ++ ;
        dfs(root);

        printf("%d\n", min(f[root][0], f[root][1]));
    }

    return 0;
}

AcWing 1077. 皇宫看守

题目

传送门:AcWing 1077. 皇宫看守
动态规划-树形DP-习题_第11张图片
输入样例:

6
1 30 3 2 3 4
2 16 2 5 6
3 5 0
4 4 0
5 11 0
6 5 0

输出样例:

25

样例解释:
在2、3、4结点安排护卫,可以观察到全部宫殿,所需经费最少,为 16 + 5 + 4 = 25。

题解

这个题初看与AcWing 323. 战略游戏 ,但是仔细分析,发现战略游戏的不选的点中间最多只能空一个,所以只需要记录两个状态,但是这个题可以出现不选的点中间只能空;两个,例如A->B->C->D,可以选择A和D,所以这里需要记录三个状态

 * 分为3种状态
 * f[i][0] 表示节点i被父结点看到的所有情况中的最小花费
 * f[i][1] 表示节点i被子节点看到的所有情况中的最小花费
 * f[i][2] 表示节点i上摆放侍卫的所用情况中的最小花费
 * 
 * 状态计算:
 * f[i][0] = min (f[j][2], f[j][1]) 点i是没有侍卫的,因此子节点只能有这两种情况
 * f[i][2] = min (f[j][2], f[j][0], f[j][1]) 点i有侍卫,子节点可以被i影响到,因此有3种情况
 * 
 * f[i][1] = min(f[j][1], f[j][2]) + f[k][2]
 *3种情况较为复杂:如果结点i被子节点看到,那么说明,子节点有侍卫,i没有侍卫
 * 那么在所有的集合情况中,应该找到子节点有侍卫的最小花费假定为f[k][2], 再加上其他子节点的花费情况,
 * 而其他子节点的情况为min(f[j][1], f[j][2])(由于i没有侍卫,子节点可以表示为放侍卫或者被子节点影响)
 * 
 * 观察第一种状态的计算,f[i][0] = min (f[j][2], f[j][1]), 
 * 其实可以得出第3种状态中的min(f[j][1], f[j][2])就相当于f[i][0] 减去 找到的某一个子节点的最小花费 
 * (因为第一种状态是包含了所有子节点的情况, 而当前找出除开某个子节点的其他情况)
 * (在这种情况下,要除去的子节点的最小花费下相当于min(f[j][1], f[j][2]), 因为父亲节点是没有侍卫的)
 * 因此 f[i][1] = min (f[i][1], f[i][0] - min(f[j][1], f[j][2]) + f[j][2])

代码

// https://www.acwing.com/activity/content/code/content/1222553/
#include 
#include 
#include 

using namespace std;

const int N = 1510;

int n;
int h[N], w[N], e[N], ne[N], idx;
int f[N][3];
bool st[N];

void add(int a, int b)
{
     
    e[idx] = b, ne[idx] = h[a], h[a] = idx ++ ;
}

void dfs(int u)
{
     
    f[u][2] = w[u];

    int sum = 0;
    for (int i = h[u]; ~i; i = ne[i])
    {
     
        int j = e[i];
        dfs(j);
        f[u][0] += min(f[j][1], f[j][2]);
        f[u][2] += min(min(f[j][0], f[j][1]), f[j][2]);
        sum += min(f[j][1], f[j][2]);
    }

    f[u][1] = 1e9;
    for (int i = h[u]; ~i; i = ne[i])
    {
     
        int j = e[i];
        f[u][1] = min(f[u][1], sum - min(f[j][1], f[j][2]) + f[j][2]);
    }
}

int main()
{
     
    cin >> n;

    memset(h, -1, sizeof h);
    for (int i = 1; i <= n; i ++ )
    {
     
        int id, cost, cnt;
        cin >> id >> cost >> cnt;
        w[id] = cost;
        while (cnt -- )
        {
     
            int ver;
            cin >> ver;
            add(id, ver);
            st[ver] = true;
        }
    }

    int root = 1;
    while (st[root]) root ++ ;

    dfs(root);

    cout << min(f[root][1], f[root][2]) << endl;

    return 0;
}

你可能感兴趣的:(算法,动态规划,树形DP)