285. 没有上司的舞会 - AcWing题库树形dp的一道题2024-02-07(计数类DP、数位统计DP、状态压缩DP、树形 DP、记忆化搜索)-CSDN博客
1072. 树的最长路径 - AcWing题库
找树的直径
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);
}
}
1073. 树的中心 - AcWing题库
暴力枚举:求出每个点到其他所有点的最远距离,然后取一个最小值
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);
}
}
1075. 数字转换 - AcWing题库
每一个数的约数之和(不包括本身)都是固定的。将一个数(能转换)的约数之和作为它的父节点。那么问题就转化为 求树的最长路径问题。
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);
}
}
1074. 二叉苹果树 - AcWing题库
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]);
}
}
323. 战略游戏 - AcWing题库
和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]));//找到两个状态最小的那一个
}
}
}
1077. 皇宫看守 - AcWing题库
战略游戏那道题看的是边,皇宫看守这道题看的是点。
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]));
}
}