2.17
2.17
P1352 没有上司的舞会 - 洛谷 (luogu.com.cn)
-
\(dp[i][1/0]\)表示以i结点为根节点时,选或者不选所得到最大幸福值
-
我们dfs到叶子结点后在回溯时进行dp
-
假如我们选择了boss,那么boss的子节点就不能再选,\(dp[parent][1] += dp[son][0]\),而没有选择时,有两种可能,\(dp[parent][0] = Math.max(dp[son][1], dp[son][0])\)
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
public class Main implements Runnable {
static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
static final int N = (int) (6e3 + 10);
static Vector<Integer>[] mans = new Vector[N];
static int n;
static int[] happy = new int[N];
static boolean[] hasFather = new boolean[N];
static int[][] dp = new int[N][2];
public static void main(String[] args) {
new Thread(null, new Main(), "", 1 << 29).start();
}
public static int nextInt() throws IOException {
cin.nextToken();
return (int) cin.nval;
}
public static void dfs(int root) {
dp[root][1] = happy[root];
for (Integer son : mans[root]) {
dfs(son);
dp[root][1] += dp[son][0];
dp[root][0] += Math.max(dp[son][1], dp[son][0]);
}
}
@Override
public void run() {
try {
n = nextInt();
for (int i = 1; i <= n; i++) {
happy[i] = nextInt();
mans[i] = new Vector<>();
}
for (int i = 1; i <= n - 1; i++) {
int l = nextInt(); int k = nextInt();
mans[k].add(l);
hasFather[l] = true;
}
int root = 0;
for (int i = 1; i <= n; i++) {
if (!hasFather[i]) {
root = i;
break;
}
}
dfs(root);
cout.println(Math.max(dp[root][1], dp[root][0]));
cout.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
[P2014 CTSC1997] 选课 - 洛谷 (luogu.com.cn)
- 其实是一个背包问题
- 定义\(dp[i][j]\)为以i结点为根节点,容量为j的的最大得分,因为本题拥有前提条件,即只有我们在父结点选择后,子节点才能跟着选择,所以考虑组合背包
- 把每一个结点下的子树看作一组背包,在保证父结点体积的情况下,增加子树容量,即\(dp[root][j] = max(dp[root][j], dp[son][0],dp[son][1],dp[son][2].....dp[son][j - weight[root]])\),所以我们只需要遍历容量j和k(\(0<=k<=j-weight[root]\))
- dp的原则就是将一个大的问题拆分为无数的小问题,再将小问题得到的结果迭代成大结果
- 刚开始还在疑惑为什么可以这样做,其实就是如果我们要迭代到最高的点,我们肯定需要子节点的答案,那么直到到达叶子结点后,我们才能层层返回答案给叶子结点
- 记得在dfs搜索到叶子结点时赋初值,注意在我们dfs下去时就对dp就行初始化
- 注意的是我们得到的其实是森林,理想状态下我们需要把他转为一棵树,那么我们只要添加一个结点来成为这片森林的根节点的父结点即可
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
public class Main implements Runnable {
static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
static final int N = 310;
static Vector<Integer>[] node = new Vector[N];
static int[] val = new int[N];
static int n, m;
static int[][] dp = new int[N][N];// 以i结点为根节点,容量为j的的最大得分
static int[] v = new int[N];
public static void main(String[] args) {
new Thread(null, new Main(), "", 1 << 29).start();
}
public static int nextInt() throws IOException {
cin.nextToken();
return (int) cin.nval;
}
public static void dfs(int parent) {
for (int i = 1; i <= m; i++) {
dp[parent][i] = val[parent];
}
for (Integer son : node[parent]) {
dfs(son);
for (int j = m; j >= v[parent]; j--) {
for (int k = 0; k <= j - v[parent]; k++) {
dp[parent][j] = Math.max(dp[parent][j], dp[son][k] + dp[parent][j - k]);
}
}
}
}
@Override
public void run() {
try {
n = nextInt();m = nextInt();
for (int i = 0; i <= n; i++) node[i] = new Vector<>();
for (int i = 1; i <= n; i++) {
int parent = nextInt();
val[i] = nextInt();v[i] = 1;
node[parent].add(i);
}
dfs(0);
cout.println(dp[0][m]);
cout.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
P2015 二叉苹果树 - 洛谷 (luogu.com.cn)
- 这个做法可能有点不同,题解基本是用边来作为背包容量,我是将边转为点数,考虑到一条边肯定连两个点,然后保留q条边,那么最后肯定有q+1个点
- 由于点是小于100的,所以采用疏密图来存储边权也是ok的
- 本题\(dp[i][j]\)为以i为根节点的子树在保留j个结点前的最大苹果数和
- dfs赋值时为什么是加\(val[parent][pre]\)呢,因为我们在达到这个点时,是默认该点父结点是保留下来了的(不保留也就不可能到达当前结点),那么在这棵树中也一定保留了当前的这个边,所以先加上方便后续迭代
- 后续dp就和上一题一个思路了
- 其实应该要用数学公式来详细证明这个代码的可靠性,但是笔者实力有限,不太会了......
- 感觉有点投机取巧了这个题
import java.io.*;
import java.util.*;
import java.util.zip.ZipEntry;
public class Main implements Runnable {
static StreamTokenizer cin = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
static PrintWriter cout = new PrintWriter(new OutputStreamWriter(System.out));
static final int N = 110;
static Vector<Integer>[] node = new Vector[N];
static int n, q, m;
static int[][] val = new int[N][N];
static int[][] dp = new int[N][N];
public static void main(String[] args) {
new Thread(null, new Main(), "", 1 << 29).start();
}
public static int nextInt() throws IOException {
cin.nextToken();
return (int) cin.nval;
}
public static void dfs(int parent, int pre) {
for (int i = 1; i <= m; i++) {
dp[parent][i] = val[parent][pre];
}
for (Integer son : node[parent]) {
if (son == pre) continue;
dfs(son, parent);
for (int j = m; j >= 1; j--) {
for (int k = 0; k <= j - 1; k++) {
dp[parent][j] = Math.max(dp[parent][j], dp[son][k] + dp[parent][j - k]);
}
}
}
}
@Override
public void run() {
try {
n = nextInt();q = nextInt();
for (int i = 1; i <= n; i++) node[i] = new Vector<>();
for (int i = 1; i <= n - 1; i++) {
int x = nextInt();int y = nextInt(); int v = nextInt();
node[x].add(y);node[y].add(x);
val[x][y] = v;val[y][x] = v;
}
m = q + 1;
dfs(1, -1);
cout.println(dp[1][m]);
cout.flush();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
浙公网安备 33010602011771号