赞
踩
285. 没有上司的舞会 - AcWing题库树形dp的一道题2024-02-07(计数类DP、数位统计DP、状态压缩DP、树形 DP、记忆化搜索)-CSDN博客
找树的直径
1、任取一点作为起点,找到距离该点最远的点u BFS, DFS
2、再找到距离u点最远的点v BFS, DFS
那么u和v之间的路径就是一条直径
- import java.util.*;
-
- public class Main{
- static int N = 10010, M = 2 * N;//因为是无向图,所以要开两倍的数组
- static int[] h = new int[M], ne = new int[M], e = new int[M], w = new int[M];
- static int n, res, idx;//res表示答案
-
- //邻接表存储无向图
- public static void add(int a, int b, int c){
- e[idx] = b;
- w[idx] = c;
- ne[idx] = h[a];
- h[a] = idx ++;
- }
-
- public static int dfs(int u, int father){
- int dist = 0;//表示这个点往下走的最大长度
- int d1 = 0;//这个点往下走的最大长度
- int d2 = 0;//这个点往下走的第二大长度
- for(int i = h[u]; i != -1; i = ne[i]){
- int j = e[i];
- if(j == father) continue;//由于只能往下走,如果等于父节点就说明往回走了,所以不行
-
- int d = dfs(j, u) + w[i];
- dist = Math.max(dist, d);
- if(d > d1){
- d2 = d1;
- d1 = d;
- }else if(d > d2){
- d2 = d;
- }
- }
-
- res = Math.max(res, d1 + d2);
- return dist;
- }
-
- //开始main函数
- public static void main(String[] args){
- Scanner sc = new Scanner(System.in);
- n = sc.nextInt();
-
- Arrays.fill(h, -1);
- for(int i = 1; i < n; i ++){//一共有n-1条边
- int a = sc.nextInt();
- int b = sc.nextInt();
- int c = sc.nextInt();
- add(a, b, c);
- add(b, a, c);
- }
-
- dfs(1, -1);//取编号为1的点作为根节点,由于其没有父节点,所以让其父节点的参数为-1
-
- System.out.print(res);
- }
- }
暴力枚举:求出每个点到其他所有点的最远距离,然后取一个最小值
- import java.util.*;
-
- public class Main{
- static int N = 10010, M = 2 * N, idx, n, INF = 0x3f3f3f3f;
- static int[] h = new int[M], ne = new int[M], e = new int[M], w = new int[M];//邻接表
- static int[] d1 = new int[M];//往下走最长距离
- static int[] d2 = new int[M];//往下走次长距离
- static int[] p = new int[M];//记录最长路径是由哪个节点过来的
- static int[] up = new int[M];//往上走的最长距离
-
- //邻接表存储
- public static void add(int a, int b, int c){
- e[idx] = b;
- w[idx] = c;
- ne[idx] = h[a];
- h[a] = idx ++;
- }
-
- //往下走找最长距离
- public static int dfs_down(int u, int father){
- d1[u] = 0;
- d2[u] = 0;
-
- for(int i = h[u]; i != -1; i = ne[i]){
- int j = e[i];
- if(j == father) continue;
-
- int d = dfs_down(j, u) + w[i];
-
- if(d > d1[u]){
- d2[u] = d1[u];
- d1[u] = d;
- p[u] = j;
- }else if(d > d2[u]){
- d2[u] = d;
- }
- }
-
- return d1[u];
- }
-
- //往上走的最长距离
- //有两个方向:父节点接着往上走,父节点往下走
- public static void dfs_up(int u, int father){
- for(int i = h[u]; i != -1; i = ne[i]){
- int j = e[i];
- if(j == father) continue;
-
- //如果父节点往下走的最长距离是经由这个节点来的,那么就用次长和往上走的距离比较,最后要加上这个点到父节点的距离
- if(p[u] == j) up[j] = Math.max(d2[u], up[u]) + w[i];
- else up[j] = Math.max(d1[u], up[u]) + w[i];
-
- dfs_up(j, u);
- }
- }
-
- //开始main函数
- public static void main(String[] args){
- Scanner sc = new Scanner(System.in);
- n = sc.nextInt();
-
- Arrays.fill(h, -1);//初始化
-
- for(int i = 1; i < n; i ++){
- int a = sc.nextInt();
- int b = sc.nextInt();
- int c = sc.nextInt();
- 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 = Math.min(res, Math.max(up[i], d1[i]));//找到每个点的两个方向的最大值,再从中取最小
- }
-
- System.out.print(res);
- }
- }
每一个数的约数之和(不包括本身)都是固定的。将一个数(能转换)的约数之和作为它的父节点。那么问题就转化为 求树的最长路径问题。
- import java.util.*;
-
- public class Main{
- static int N = 50010;
- static int[] h = new int[N], ne = new int[N], e = new int[N];
- static int idx, n, res;
- static boolean[] st = new boolean[N];
- static int[] sum = new int[N];//约数之和
-
- //邻接表存储
- public static void add(int a, int b){
- e[idx] = b;
- ne[idx] = h[a];
- h[a] = idx ++;
- }
-
- //dfs树的最长路径模板
- public static int dfs(int u){
- int d1 = 0;
- int d2 = 0;
- for(int i = h[u]; i != -1; i = ne[i]){
- int j = e[i];
-
- int d = dfs(j) + 1;
- if(d > d1){
- d2 = d1;
- d1 = d;
- }else if(d > d2){
- d2 = d;
- }
- }
- res = Math.max(res, d1 + d2);
- return d1;
- }
-
- public static void main(String[] args){
- Scanner sc = new Scanner(System.in);
- n = sc.nextInt();
-
- Arrays.fill(h, -1);//初始化
-
- //遍历1到n,判断这个数可以是哪些数的约数
- for(int i = 1; i <= n; i ++){
- for(int j = 2; j <= n / i; j ++){
- sum[i * j] += i;
- }
- }
-
- for(int i = 1; i <= n; i ++){
- if(i > sum[i]){
- add(sum[i], i);//建立有向图,约数之和为其父节点
- st[i] = true;//有父节点
- }
- }
-
- for(int i = 1; i <= n; i ++){
- if(!st[i]) dfs(i);//如果没有父节点的话,就遍历这一颗树
- }
-
- dfs(1);
- System.out.print(res);
- }
- }
- import java.util.*;
-
- public class Main{
- static int N = 110, M = 2 * N;
- static int[] h = new int[M], ne = new int[M], e = new int[M];
- static int idx, n, m;
- static int[][] f = new int[M][M];//f[u][j]以u为根节点的树选j条边的最大价值
- static int[] w = new int[M];//每一条边的价值
-
- //邻接表存储
- public static void add(int a, int b, int c){
- e[idx] = b;
- w[idx] = c;
- ne[idx] = h[a];
- h[a] = idx ++;
- }
-
- public static void dfs(int u, int father){
- for(int i = h[u]; i != -1; i = ne[i]){//先枚举物品组
- int son = e[i];
- if(son == father) continue;//由于是无向图,所以要保证不往回走
-
- dfs(son, u);//递归!!!进行子节点的遍历
- for(int j = m; j >= 0; j --){//再从大到小枚举体积
- for(int k = 0; k < j; k ++){//最后再枚举决策
- //这里j先减去1,是因为要留一条边给连接他的父节点
- f[u][j] = Math.max(f[u][j], f[u][j - 1 - k] + f[son][k] + w[i]);
- }
- }
- }
- }
-
- public static void main(String[] args){
- Scanner sc = new Scanner(System.in);
- n = sc.nextInt();
- m = sc.nextInt();
-
- Arrays.fill(h, -1);
- for(int i = 1; i < n; i ++){
- int a = sc.nextInt();
- int b = sc.nextInt();
- int c = sc.nextInt();
- add(a, b, c);//建立无向图
- add(b, a, c);
- }
-
- dfs(1, -1);
- System.out.print(f[1][m]);
- }
- }
和285. 没有上司的舞会 - AcWing题库 这道题很像2024-02-07(计数类DP、数位统计DP、状态压缩DP、树形 DP、记忆化搜索)-CSDN博客
没有上司的舞会:每条边上最多选一个点,求最大权值 。
战略游戏:每条边上至少选一个点,求最小权值。
2. indexOf()方法的使用,截取字符串,字符串截取,切割字符串,split(),join(),Replace()_val.indexof-CSDN博客
当题目说有多组测试数据,但又没有说明有多少组时,用while(sc.hasNext())
- import java.util.*;
-
- public class Main{
- static int N = 1510;
- static int[] h = new int[N], ne = new int[N], e = new int[N];
- static int idx, n;
- static int[][] f = new int[N][2];//f[u][0] f[u][1]
- static boolean[] st = new boolean[N];//用来判断根节点
-
- //邻接表存储
- public static void add(int a, int b){
- e[idx] = b;
- ne[idx] = h[a];
- h[a] = idx ++;
- }
-
- public static void dfs(int u){
- //f[u][0] = 0;//初始化
- f[u][1] = 1;
-
- for(int i = h[u]; i != -1; i = ne[i]){
- int j = e[i];
-
- dfs(j);//递归
- //dp思想分析
- f[u][0] += f[j][1];
- f[u][1] += Math.min(f[j][1], f[j][0]);
- }
- }
-
- public static void main(String[] args){
- Scanner sc = new Scanner(System.in);
-
- while(sc.hasNext()){//多组测试数据
- n = sc.nextInt();
-
- //由于有多组测试数据,所以每次都要进行初始化
- idx = 0;
- Arrays.fill(h, -1);
- Arrays.fill(st, false);
-
- for(int i = 0; i < n; i ++){
- String str = sc.next();
- //输入
- int id = Integer.parseInt(str.substring(0, str.indexOf(":")));
- int cnt = Integer.parseInt(str.substring(str.indexOf('(') + 1, str.length() - 1));
- for(int j = 1; j <= cnt; j ++){
- int b = sc.nextInt();
- add(id, b);
- st[b] = true;
- }
- }
-
- int root = 0;
- while(st[root]) root ++;
- dfs(root);
-
- System.out.println(Math.min(f[root][1], f[root][0]));//找到两个状态最小的那一个
- }
- }
- }
战略游戏那道题看的是边,皇宫看守这道题看的是点。
- import java.util.*;
-
- public class Main{
- static int N = 1510;
- static int n, idx;
- static int[] h = new int[N], ne = new int[N], e = new int[N];
- static int[] w = new int[N];
- static boolean[] st = new boolean[N];
- static int[][] f = new int[N][3];
- //f[u][0]表示点u没有放警卫,且点u被父节点看到的所有方案中的最小花费
- //f[u][1]表示点u没有放警卫,且点u被子节点看到的所有方案中的最小花费
- //f[u][2]表示在点u上放警卫的所有方案中的最小花费
-
- public static void add(int a, int b){
- e[idx] = b;
- ne[idx] = h[a];
- h[a] = idx ++;
- }
-
- public static void dfs(int u){
- f[u][2] = w[u];//放警卫的花费
- int sum = 0;
-
- for(int i = h[u]; i != -1; i = ne[i]){
- int j = e[i];
-
- dfs(j);//递归
- f[u][0] += Math.min(f[j][1], f[j][2]);//u被父节点看到,u的子节点的情况
- sum += Math.min(f[j][1], f[j][2]);//用于f[u][0]的计算,其实这里的sum可以不用要,因为已经记录在f[u][0]里面了
- f[u][2] += Math.min(Math.min(f[j][0], f[j][1]), f[j][2]);//u放警卫,u的儿子节点的情况
- }
-
- f[u][1] = 0x3f3f3f3f;//要求最小值,先置为无穷大
- for(int i = h[u]; i != -1; i = ne[i]){//自己放警卫的情况,要讨论它的所有子节点
- int j = e[i];//当枚举到字节点j,子节点j放警卫,其他子节点放与不放都可以,找到最小花费的
- f[u][1] = Math.min(f[u][1], f[j][2] + (sum - Math.min(f[j][1], f[j][2])));
- }
- }
-
- public static void main(String[] args){
- Scanner sc = new Scanner(System.in);
- n = sc.nextInt();
-
- Arrays.fill(h, -1);
- for(int i = 1; i <= n; i ++){
- int a = sc.nextInt();
- w[a] = sc.nextInt();
- int cnt = sc.nextInt();
- while(cnt -- > 0){
- int b = sc.nextInt();
- add(a, b);
- st[b] = true;
- }
- }
-
- int root = 1;
- while(st[root]) root ++;
-
- dfs(root);
-
- System.out.print(Math.min(f[root][1], f[root][2]));
- }
- }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。