文章目录
——2020年12月 4日(周五)——————————————————
沉思……了这么多天都沉思了些什么?
那就是试着自己做出树形DP的几道题目……
至于结果嘛……
没错我失败了……
不得不承认,没有跨过所谓的那一道 ”坎“ 真的很难啊……浪费了一周时间了
#树形DP
一、没有上司的舞会
285. 没有上司的舞会
注释版代码
#include<bits/stdc++.h>
using namespace std;
const int N = 6010;
int f[N][2];
int happy[N];
bool b[N];
int h[N], ne[N], e[N], idx;
//上司a,下属b
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void dfs(int x){
f[x][1] = happy[x];
for(int i = h[x]; i != -1; i = ne[i]){
int j = e[i];
//这里是个递归的过程,和我一开始想的不太一样。
dfs(j);
//x不来(不选x)
f[x][0] += max(f[j][0], f[j][1]);
//x来啦
f[x][1] += f[j][0];
}
}
int main(){
memset(h, -1, sizeof h);
int n; cin >> n;
for(int i = 1; i <= n; i ++) scanf("%d", &happy[i]);
for(int i = 1; i < n; i ++){
//下属,上司
int l, k; scanf("%d%d", &l, &k);
//记录节点状态,如果有粑粑了,就不会是根节点了啊
b[l] = 1;
//上属,下属
add(k, l);
}
//找到根节点
int root = 1;
while(b[root]) root ++;
//从根节点开始出发,一路搜下去(记忆化搜索)
dfs(root);
//最后的结果会在根节点选或不选之间。
printf("%d", max(f[root][0], f[root][1]));
return 0;
}
纯净版代码
#include<bits/stdc++.h>
using namespace std;
const int N = 6010;
int f[N][2];
int happy[N];
bool b[N];
int h[N], ne[N], e[N], idx;
void add(int a, int b){
e[idx] = b, ne[idx] = h[a], h[a] = idx ++;
}
void dfs(int x){
f[x][1] = happy[x];
for(int i = h[x]; i != -1; i = ne[i]){
int j = e[i];
dfs(j);
f[x][0] += max(f[j][0], f[j][1]);
f[x][1] += f[j][0];
}
}
int main(){
memset(h, -1, sizeof h);
int n; cin >> n;
for(int i = 1; i <= n; i ++) scanf("%d", &happy[i]);
for(int i = 1; i < n; i ++){
int l, k; scanf("%d%d", &l, &k);
b[l] = 1;
add(k, l);
}
int root = 1;
while(b[root]) root ++;
dfs(root);
printf("%d", max(f[root][0], f[root][1]));
return 0;
}
——2020年12月 5(26)日(周六)——————————————————
隔了三周了,才有机会再次学习树形DP
(看着三周前的学习记录感觉还行,就是当时很难理解心态有点崩,于是用上了很多奇奇怪怪的语言……请忽略
二、二叉苹果树
DP就是用来记录某个状态下的最优解的。
树形DP不用枚举状态而是搜索状态,本题就是用了深搜的思想进行搜索;
从根节点开始往下走,一步步地记录各个状态下的最优解,最后供目标状态使用。
注释版代码
下面的代码用的是邻接表储存的树,有个好处就是不论是多少叉的树都可以储存,不局限于本题的二叉树。
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int f[N][N];
int n, q;
int sum[N];
//树的储存,随便用什么只要能够储存就可以了。这里用的是邻接表,个人习惯(因为只会这个QwQ)
int idx, h[N], nxt[2 * N], e[2 * N], w[2 * N];
void add(int a, int b, int c){
e[idx] = b, w[idx] = c, nxt[idx] = h[a], h[a] = idx ++;
}
void dfs(int now, int dad){
for(int i = h[now]; i != -1; i = nxt[i]){
int son = e[i], w_son = w[i];
if(son == dad) continue;
dfs(son, now);
sum[now] += (sum[son] + 1);
//这个子树上留几条边?从大到小排列!要是超过了要留的最大边数就用要留的最大边数,没超过就算了。
//这样枚举是为了不重复使用一棵子树
//由于每次枚举子树都是从左往右枚举过来的,那么当前f[now][v_now]中的值都是前面的子树造成的
//这样倒过来枚举,每一棵子树,它只会被上一轮的值影响到。
for(int j = min(q, sum[now]); j > 0; j --)
//给子树留几条边?最多给它留的边只有j - 1条
for(int k = 0; k <= j - 1; k ++)
f[now][j] = max(f[now][j], f[now][j - 1 - k] + f[son][k] + w_son);
}
}
int main(){
memset(f, 0, sizeof f);
memset(h, -1, sizeof h);
cin >> n >> q;
for(int i = 1; i < n; i ++){
int u, v, w1; scanf("%d%d%d", &u, &v, &w1);
add(u, v, w1); add(v, u, w1);
}
f[1][0] = 0;
dfs(1, 0);
cout << f[1][q];
return 0;
}
纯净版代码
#include<bits/stdc++.h>
using namespace std;
const int N = 110;
int f[N][N];
int n, q;
int sum[N];
int idx, h[N], nxt[2 * N], e[2 * N], w[2 * N];
void add(int a, int b, int c){
e[idx] = b, w[idx] = c, nxt[idx] = h[a], h[a] = idx ++;
}
void dfs(int now, int dad){
for(int i = h[now]; i != -1; i = nxt[i]){
int son = e[i], w_son = w[i];
if(son == dad) continue;
dfs(son, now);
sum[now] += (sum[son] + 1);
for(int j = min(q, sum[now]); j > 0; j --)
for(int k = 0; k <= j - 1; k ++)
f[now][j] = max(f[now][j], f[now][j - 1 - k] + f[son][k] + w_son);
}
}
int main(){
memset(f, 0, sizeof f);
memset(h, -1, sizeof h);
cin >> n >> q;
for(int i = 1; i < n; i ++){
int u, v, w1; scanf("%d%d%d", &u, &v, &w1);
add(u, v, w1); add(v, u, w1);
}
f[1][0] = 0;
dfs(1, 0);
cout << f[1][q];
return 0;
}
后来我又找到了之前做的代码……应该是当时懒得写笔记了。
其实都是差不多的啦。
#include<bits/stdc++.h>
using namespace std;
const int N = 110, M = 220;
int f[N][N];
int e[M], w[M], ne[M], h[M], idx;
int n, m;
void add(int a, int b, int w1){
e[idx] = b, w[idx] = w1, ne[idx] = h[a], h[a] = idx ++;
}
void dfs(int u, int fa){
for(int i = h[u]; i != -1; i = ne[i]){
if(e[i] == fa) continue;
dfs(e[i], u);
for(int j = m; j >= 0; j --)
for(int k = 0; k + 1 <= j; k ++)
f[u][j] = max(f[u][j], f[e[i]][k] + f[u][j - 1 - k] + w[i]);
}
}
int main(){
memset(h, -1, sizeof h);
cin >> n >> m;
for(int i = 1; i < n; i ++){
int a, b, w1;
scanf("%d%d%d", &a, &b, &w1);
add(a, b, w1); add(b, a, w1);
}
dfs(1, -1);
printf("%d", f[1][m]);
return 0;
}