当前位置:   article > 正文

2024-02-19(树形DP)

2024-02-19(树形DP)

285. 没有上司的舞会 - AcWing题库树形dp的一道题2024-02-07(计数类DP、数位统计DP、状态压缩DP、树形 DP、记忆化搜索)-CSDN博客

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

找树的直径

1、任取一点作为起点,找到距离该点最远的点u  BFS, DFS

2、再找到距离u点最远的点v   BFS, DFS

        那么u和v之间的路径就是一条直径 

  1. import java.util.*;
  2. public class Main{
  3. static int N = 10010, M = 2 * N;//因为是无向图,所以要开两倍的数组
  4. static int[] h = new int[M], ne = new int[M], e = new int[M], w = new int[M];
  5. static int n, res, idx;//res表示答案
  6. //邻接表存储无向图
  7. public static void add(int a, int b, int c){
  8. e[idx] = b;
  9. w[idx] = c;
  10. ne[idx] = h[a];
  11. h[a] = idx ++;
  12. }
  13. public static int dfs(int u, int father){
  14. int dist = 0;//表示这个点往下走的最大长度
  15. int d1 = 0;//这个点往下走的最大长度
  16. int d2 = 0;//这个点往下走的第二大长度
  17. for(int i = h[u]; i != -1; i = ne[i]){
  18. int j = e[i];
  19. if(j == father) continue;//由于只能往下走,如果等于父节点就说明往回走了,所以不行
  20. int d = dfs(j, u) + w[i];
  21. dist = Math.max(dist, d);
  22. if(d > d1){
  23. d2 = d1;
  24. d1 = d;
  25. }else if(d > d2){
  26. d2 = d;
  27. }
  28. }
  29. res = Math.max(res, d1 + d2);
  30. return dist;
  31. }
  32. //开始main函数
  33. public static void main(String[] args){
  34. Scanner sc = new Scanner(System.in);
  35. n = sc.nextInt();
  36. Arrays.fill(h, -1);
  37. for(int i = 1; i < n; i ++){//一共有n-1条边
  38. int a = sc.nextInt();
  39. int b = sc.nextInt();
  40. int c = sc.nextInt();
  41. add(a, b, c);
  42. add(b, a, c);
  43. }
  44. dfs(1, -1);//取编号为1的点作为根节点,由于其没有父节点,所以让其父节点的参数为-1
  45. System.out.print(res);
  46. }
  47. }

 

1073. 树的中心 - AcWing题库

暴力枚举:求出每个点到其他所有点的最远距离,然后取一个最小值 

  1. import java.util.*;
  2. public class Main{
  3. static int N = 10010, M = 2 * N, idx, n, INF = 0x3f3f3f3f;
  4. static int[] h = new int[M], ne = new int[M], e = new int[M], w = new int[M];//邻接表
  5. static int[] d1 = new int[M];//往下走最长距离
  6. static int[] d2 = new int[M];//往下走次长距离
  7. static int[] p = new int[M];//记录最长路径是由哪个节点过来的
  8. static int[] up = new int[M];//往上走的最长距离
  9. //邻接表存储
  10. public static void add(int a, int b, int c){
  11. e[idx] = b;
  12. w[idx] = c;
  13. ne[idx] = h[a];
  14. h[a] = idx ++;
  15. }
  16. //往下走找最长距离
  17. public static int dfs_down(int u, int father){
  18. d1[u] = 0;
  19. d2[u] = 0;
  20. for(int i = h[u]; i != -1; i = ne[i]){
  21. int j = e[i];
  22. if(j == father) continue;
  23. int d = dfs_down(j, u) + w[i];
  24. if(d > d1[u]){
  25. d2[u] = d1[u];
  26. d1[u] = d;
  27. p[u] = j;
  28. }else if(d > d2[u]){
  29. d2[u] = d;
  30. }
  31. }
  32. return d1[u];
  33. }
  34. //往上走的最长距离
  35. //有两个方向:父节点接着往上走,父节点往下走
  36. public static void dfs_up(int u, int father){
  37. for(int i = h[u]; i != -1; i = ne[i]){
  38. int j = e[i];
  39. if(j == father) continue;
  40. //如果父节点往下走的最长距离是经由这个节点来的,那么就用次长和往上走的距离比较,最后要加上这个点到父节点的距离
  41. if(p[u] == j) up[j] = Math.max(d2[u], up[u]) + w[i];
  42. else up[j] = Math.max(d1[u], up[u]) + w[i];
  43. dfs_up(j, u);
  44. }
  45. }
  46. //开始main函数
  47. public static void main(String[] args){
  48. Scanner sc = new Scanner(System.in);
  49. n = sc.nextInt();
  50. Arrays.fill(h, -1);//初始化
  51. for(int i = 1; i < n; i ++){
  52. int a = sc.nextInt();
  53. int b = sc.nextInt();
  54. int c = sc.nextInt();
  55. add(a, b, c);
  56. add(b, a, c);
  57. }
  58. dfs_down(1, -1);//往下走
  59. dfs_up(1, -1);//往上走
  60. int res = INF;
  61. for(int i = 1; i <= n; i ++){
  62. res = Math.min(res, Math.max(up[i], d1[i]));//找到每个点的两个方向的最大值,再从中取最小
  63. }
  64. System.out.print(res);
  65. }
  66. }

 

1075. 数字转换 - AcWing题库

每一个数的约数之和(不包括本身)都是固定的。将一个数(能转换)的约数之和作为它的父节点。那么问题就转化为 求树的最长路径问题。 

  1. import java.util.*;
  2. public class Main{
  3. static int N = 50010;
  4. static int[] h = new int[N], ne = new int[N], e = new int[N];
  5. static int idx, n, res;
  6. static boolean[] st = new boolean[N];
  7. static int[] sum = new int[N];//约数之和
  8. //邻接表存储
  9. public static void add(int a, int b){
  10. e[idx] = b;
  11. ne[idx] = h[a];
  12. h[a] = idx ++;
  13. }
  14. //dfs树的最长路径模板
  15. public static int dfs(int u){
  16. int d1 = 0;
  17. int d2 = 0;
  18. for(int i = h[u]; i != -1; i = ne[i]){
  19. int j = e[i];
  20. int d = dfs(j) + 1;
  21. if(d > d1){
  22. d2 = d1;
  23. d1 = d;
  24. }else if(d > d2){
  25. d2 = d;
  26. }
  27. }
  28. res = Math.max(res, d1 + d2);
  29. return d1;
  30. }
  31. public static void main(String[] args){
  32. Scanner sc = new Scanner(System.in);
  33. n = sc.nextInt();
  34. Arrays.fill(h, -1);//初始化
  35. //遍历1到n,判断这个数可以是哪些数的约数
  36. for(int i = 1; i <= n; i ++){
  37. for(int j = 2; j <= n / i; j ++){
  38. sum[i * j] += i;
  39. }
  40. }
  41. for(int i = 1; i <= n; i ++){
  42. if(i > sum[i]){
  43. add(sum[i], i);//建立有向图,约数之和为其父节点
  44. st[i] = true;//有父节点
  45. }
  46. }
  47. for(int i = 1; i <= n; i ++){
  48. if(!st[i]) dfs(i);//如果没有父节点的话,就遍历这一颗树
  49. }
  50. dfs(1);
  51. System.out.print(res);
  52. }
  53. }

 

1074. 二叉苹果树 - AcWing题库

10. 有依赖的背包问题 - AcWing题库的简化版2024-02-12(背包模型)-CSDN博客

 

  1. import java.util.*;
  2. public class Main{
  3. static int N = 110, M = 2 * N;
  4. static int[] h = new int[M], ne = new int[M], e = new int[M];
  5. static int idx, n, m;
  6. static int[][] f = new int[M][M];//f[u][j]以u为根节点的树选j条边的最大价值
  7. static int[] w = new int[M];//每一条边的价值
  8. //邻接表存储
  9. public static void add(int a, int b, int c){
  10. e[idx] = b;
  11. w[idx] = c;
  12. ne[idx] = h[a];
  13. h[a] = idx ++;
  14. }
  15. public static void dfs(int u, int father){
  16. for(int i = h[u]; i != -1; i = ne[i]){//先枚举物品组
  17. int son = e[i];
  18. if(son == father) continue;//由于是无向图,所以要保证不往回走
  19. dfs(son, u);//递归!!!进行子节点的遍历
  20. for(int j = m; j >= 0; j --){//再从大到小枚举体积
  21. for(int k = 0; k < j; k ++){//最后再枚举决策
  22. //这里j先减去1,是因为要留一条边给连接他的父节点
  23. f[u][j] = Math.max(f[u][j], f[u][j - 1 - k] + f[son][k] + w[i]);
  24. }
  25. }
  26. }
  27. }
  28. public static void main(String[] args){
  29. Scanner sc = new Scanner(System.in);
  30. n = sc.nextInt();
  31. m = sc.nextInt();
  32. Arrays.fill(h, -1);
  33. for(int i = 1; i < n; i ++){
  34. int a = sc.nextInt();
  35. int b = sc.nextInt();
  36. int c = sc.nextInt();
  37. add(a, b, c);//建立无向图
  38. add(b, a, c);
  39. }
  40. dfs(1, -1);
  41. System.out.print(f[1][m]);
  42. }
  43. }

323. 战略游戏 - AcWing题库

285. 没有上司的舞会 - AcWing题库 这道题很像2024-02-07(计数类DP、数位统计DP、状态压缩DP、树形 DP、记忆化搜索)-CSDN博客

没有上司的舞会:每条边上最多选一个点,求最大权值 。

战略游戏:每条边上至少选一个点,求最小权值。

1.  substring()方法-CSDN博客 

2.  indexOf()方法的使用,截取字符串,字符串截取,切割字符串,split(),join(),Replace()_val.indexof-CSDN博客

当题目说有多组测试数据,但又没有说明有多少组时,用while(sc.hasNext()) 

  1. import java.util.*;
  2. public class Main{
  3. static int N = 1510;
  4. static int[] h = new int[N], ne = new int[N], e = new int[N];
  5. static int idx, n;
  6. static int[][] f = new int[N][2];//f[u][0] f[u][1]
  7. static boolean[] st = new boolean[N];//用来判断根节点
  8. //邻接表存储
  9. public static void add(int a, int b){
  10. e[idx] = b;
  11. ne[idx] = h[a];
  12. h[a] = idx ++;
  13. }
  14. public static void dfs(int u){
  15. //f[u][0] = 0;//初始化
  16. f[u][1] = 1;
  17. for(int i = h[u]; i != -1; i = ne[i]){
  18. int j = e[i];
  19. dfs(j);//递归
  20. //dp思想分析
  21. f[u][0] += f[j][1];
  22. f[u][1] += Math.min(f[j][1], f[j][0]);
  23. }
  24. }
  25. public static void main(String[] args){
  26. Scanner sc = new Scanner(System.in);
  27. while(sc.hasNext()){//多组测试数据
  28. n = sc.nextInt();
  29. //由于有多组测试数据,所以每次都要进行初始化
  30. idx = 0;
  31. Arrays.fill(h, -1);
  32. Arrays.fill(st, false);
  33. for(int i = 0; i < n; i ++){
  34. String str = sc.next();
  35. //输入
  36. int id = Integer.parseInt(str.substring(0, str.indexOf(":")));
  37. int cnt = Integer.parseInt(str.substring(str.indexOf('(') + 1, str.length() - 1));
  38. for(int j = 1; j <= cnt; j ++){
  39. int b = sc.nextInt();
  40. add(id, b);
  41. st[b] = true;
  42. }
  43. }
  44. int root = 0;
  45. while(st[root]) root ++;
  46. dfs(root);
  47. System.out.println(Math.min(f[root][1], f[root][0]));//找到两个状态最小的那一个
  48. }
  49. }
  50. }

 

1077. 皇宫看守 - AcWing题库

战略游戏那道题看的是边,皇宫看守这道题看的是点。 

 

  1. import java.util.*;
  2. public class Main{
  3. static int N = 1510;
  4. static int n, idx;
  5. static int[] h = new int[N], ne = new int[N], e = new int[N];
  6. static int[] w = new int[N];
  7. static boolean[] st = new boolean[N];
  8. static int[][] f = new int[N][3];
  9. //f[u][0]表示点u没有放警卫,且点u被父节点看到的所有方案中的最小花费
  10. //f[u][1]表示点u没有放警卫,且点u被子节点看到的所有方案中的最小花费
  11. //f[u][2]表示在点u上放警卫的所有方案中的最小花费
  12. public static void add(int a, int b){
  13. e[idx] = b;
  14. ne[idx] = h[a];
  15. h[a] = idx ++;
  16. }
  17. public static void dfs(int u){
  18. f[u][2] = w[u];//放警卫的花费
  19. int sum = 0;
  20. for(int i = h[u]; i != -1; i = ne[i]){
  21. int j = e[i];
  22. dfs(j);//递归
  23. f[u][0] += Math.min(f[j][1], f[j][2]);//u被父节点看到,u的子节点的情况
  24. sum += Math.min(f[j][1], f[j][2]);//用于f[u][0]的计算,其实这里的sum可以不用要,因为已经记录在f[u][0]里面了
  25. f[u][2] += Math.min(Math.min(f[j][0], f[j][1]), f[j][2]);//u放警卫,u的儿子节点的情况
  26. }
  27. f[u][1] = 0x3f3f3f3f;//要求最小值,先置为无穷大
  28. for(int i = h[u]; i != -1; i = ne[i]){//自己放警卫的情况,要讨论它的所有子节点
  29. int j = e[i];//当枚举到字节点j,子节点j放警卫,其他子节点放与不放都可以,找到最小花费的
  30. f[u][1] = Math.min(f[u][1], f[j][2] + (sum - Math.min(f[j][1], f[j][2])));
  31. }
  32. }
  33. public static void main(String[] args){
  34. Scanner sc = new Scanner(System.in);
  35. n = sc.nextInt();
  36. Arrays.fill(h, -1);
  37. for(int i = 1; i <= n; i ++){
  38. int a = sc.nextInt();
  39. w[a] = sc.nextInt();
  40. int cnt = sc.nextInt();
  41. while(cnt -- > 0){
  42. int b = sc.nextInt();
  43. add(a, b);
  44. st[b] = true;
  45. }
  46. }
  47. int root = 1;
  48. while(st[root]) root ++;
  49. dfs(root);
  50. System.out.print(Math.min(f[root][1], f[root][2]));
  51. }
  52. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/128025
推荐阅读
相关标签
  

闽ICP备14008679号