这道题一开始是按照caioj上面的方法写的

(1)存储二叉树用结构体,记录左儿子和右儿子

(2)把边上的权值转化到点上,离根远的点上

(3)用记忆化搜索,枚举左右节点分别有多少个点,去递归

这种写法有个好处, 避免了总的树枝个数的枚举

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std; const int MAXN = 112;
struct node
{
int v, w;
node(int v = 0, int w = 0) : v(v), w(w) {}
};
struct tree
{
int l, r;
tree() { l = r = 0; }
}a[MAXN];
vector<node> g[MAXN];
int f[MAXN][MAXN], n, q; void dfs(int x, int fa)
{
REP(i, 0, g[x].size())
{
int v = g[x][i].v, w = g[x][i].w;
if(v == fa) continue;
f[v][1] = w;
if(!a[x].l) a[x].l = v;
else a[x].r = v;
dfs(v, x);
}
} int tree_dp(int x, int k)
{
if(x == 0) return 0;
if(f[x][k] != -1) return f[x][k]; int maxt = 0;
REP(i, 0, k)
{
int ls = i, rs = k - i - 1;
maxt = max(maxt, f[x][1] + tree_dp(a[x].l, ls) + tree_dp(a[x].r, rs));
}
return f[x][k] = maxt;
} int main()
{
scanf("%d%d", &n, &q);
REP(i, 0, n - 1)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
g[u].push_back(node(v,w));
g[v].push_back(node(u,w));
} memset(f, -1, sizeof(f));
dfs(1, -1);
REP(i, 1, n + 1) f[i][0] = 0;
f[1][1] = 0;
printf("%d\n", tree_dp(1, q + 1)); return 0;
}

然后看到洛谷上还有更加简洁的写法

先往下搜索,然后回溯的时候记录边的数量,然后枚举左右节点

取多少树枝,取max。

f[u][j] = max(f[u][j-k-1] + f[v][k] + w);

然后这里用到了01背包的思想

树枝可以看成从的总的重量,所以要逆序

#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std; const int MAXN = 112;
struct node
{
int v, w;
node(int v = 0, int w = 0) : v(v), w(w) {}
};
vector<node> g[MAXN];
int f[MAXN][MAXN], b[MAXN], n, q; void dfs(int u, int fa)
{
REP(i, 0, g[u].size())
{
int v = g[u][i].v, w = g[u][i].w;
if(v == fa) continue;
dfs(v, u);
b[u] += b[v] + 1;
for(int j = min(q, b[u]); j >= 0; j--)
for(int k = 0; k <= min(b[v], j - 1); k++)
f[u][j] = max(f[u][j], f[u][j-k-1] + f[v][k] + w);
}
} int main()
{
scanf("%d%d", &n, &q);
REP(i, 1, n)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
g[u].push_back(node(v,w));
g[v].push_back(node(u,w));
} dfs(1, 0);
printf("%d\n", f[1][q]); return 0;
}

做完了选课在来看这一题,又有新的感悟。

树上背包的做法适用于多叉树和二叉树,二叉树是两个物品,多叉树是多个物品。

而这道题有一点不同是权值是边。那么吸取上面写的经验。

我们可以把边的权值转移到离根远的点上,同时可以取的点数为边数+1

然后就一样啦!!

#include<cstdio>
#include<vector>
#include<algorithm>
#define REP(i, a, b) for(int i = (a); i < (b); i++)
using namespace std; const int MAXN = 112;
int f[MAXN][MAXN], n, q;
struct node
{
int v, w;
node(int v = 0, int w = 0) : v(v), w(w) {}
};
vector<node> g[MAXN]; int dfs(int u, int fa)
{
int sum = 1;
REP(i, 0, g[u].size())
{
int v = g[u][i].v, w = g[u][i].w;
if(v == fa) continue;
f[v][1] = w;
int t = dfs(v, u);
sum += t; for(int j = sum; j >= 1; j--)
for(int k = 0; k <= min(t, j - 1); k++)
f[u][j] = max(f[u][j], f[u][j-k] + f[v][k]);
}
return sum;
} int main()
{
scanf("%d%d", &n, &q);
REP(i, 0, n - 1)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
g[u].push_back(node(v, w));
g[v].push_back(node(u, w));
}
dfs(1, -1);
printf("%d\n", f[1][q+1]);
return 0;
}

洛谷 P2015 二叉苹果树 && caioj1107 树形动态规划(TreeDP)2:二叉苹果树的更多相关文章

  1. 洛谷 P1273 有线电视网 && caioj 1109 树形动态规划(TreeDP)4:比赛转播(树上分组背包总结)

    从这篇博客往前到二叉苹果树都可以用分组背包做 这依赖性的问题,都可以用于这道题类似的方法来做 表示以i为根的树中取j个节点所能得的最大价值 那么每一个子树可以看成一个组,每个组里面取一个节点,两个节点 ...

  2. 洛谷 P2015 二叉苹果树 (树上背包)

    洛谷 P2015 二叉苹果树 (树上背包) 一道树形DP,本来因为是二叉,其实不需要用树上背包来干(其实即使是多叉也可以多叉转二叉),但是最近都刷树上背包的题,所以用了树上背包. 首先,定义状态\(d ...

  3. 洛谷 P2015 二叉苹果树(codevs5565) 树形dp入门

    dp这一方面的题我都不是很会,所以来练(xue)习(xi),大概把这题弄懂了. 树形dp就是在原本线性上dp改成了在 '树' 这个数据结构上dp. 一般来说,树形dp利用dfs在回溯时进行更新,使用儿 ...

  4. 洛谷 P2701 [USACO5.3]巨大的牛棚Big Barn Label:二维数组前缀和 你够了 这次我用DP

    题目背景 (USACO 5.3.4) 题目描述 农夫约翰想要在他的正方形农场上建造一座正方形大牛棚.他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方.我们假定,他的农场划分成 N ...

  5. 洛谷 P3267 - [JLOI2016/SHOI2016]侦察守卫(树形 dp)

    洛谷题面传送门 经典题一道,下次就称这种"覆盖距离不超过 xxx 的树形 dp"为<侦察守卫模型> 我们考虑树形 \(dp\),设 \(f_{x,j}\) 表示钦定了 ...

  6. 洛谷 P7163 - [COCI2020-2021#2] Svjetlo(树形 dp)

    洛谷题面传送门 神仙级别的树形 dp. u1s1 这种代码很短但巨难理解的题简直是我的梦魇 首先这种题目一看就非常可以 DP 的样子,但直接一维状态的 DP 显然无法表示所有情况.注意到对于这类统计一 ...

  7. DP,数论————洛谷P4317 花神的数论题(求1~n二进制中1的个数和)

    玄学代码(是洛谷题解里的一位dalao小粉兔写的) //数位DP(二进制)计算出f[i]为恰好有i个的方案数. //答案为∏(i^f[i]),快速幂解决. #include<bits/stdc+ ...

  8. 动态规划 洛谷P4017 最大食物链计数——图上动态规划 拓扑排序

    洛谷P4017 最大食物链计数 这是洛谷一题普及/提高-的题目,也是我第一次做的一题 图上动态规划/拓扑排序 ,我认为这题是很好的学习拓扑排序的题目. 在这题中,我学到了几个名词,入度,出度,及没有环 ...

  9. NOIP2017提高组Day1T3 逛公园 洛谷P3953 Tarjan 强连通缩点 SPFA 动态规划 最短路 拓扑序

    原文链接https://www.cnblogs.com/zhouzhendong/p/9258043.html 题目传送门 - 洛谷P3953 题目传送门 - Vijos P2030 题意 给定一个有 ...

随机推荐

  1. C# 比较两个数据的不同

    string[] arrRate = new string[] { "op1010", "op1020", "op1030", " ...

  2. C++之指针与引用,函数和数组

    ]={,,}; //ptr是指针,该指针类型是int[3] ]=&arr; cout << **ptr << endl;//第一次解指针时得到数组地址,第二次解指针取数 ...

  3. 三级联动(ajax同步)

    html <div id="frame"></div> js $(function(){ //拼接省市区下拉框 var str = `<select ...

  4. 【算法】prim算法(最小生成树)(与Dijkstra算法的比较)

    最小生成树: 生成树的定义:给定一个无向图,如果它的某个子图中任意两个顶点都互相连通并且是一棵树,那么这棵树就叫做生成树.(Spanning Tree) 最小生成树的定义:在生成树的基础上,如果边上有 ...

  5. 51nod 1065 最小正字段和 解决办法:set存前缀和,二分插入和二分查找

    题目: 这题要求大于0的最小字段和,常规O(n)求最大字段和的方法肯定是没法解的. 我的解法是:用sum[i]存前i项的和,也就是前缀和. 这题就变成了求sum[j]-sum[i]的大于0的最小值( ...

  6. Devexpress控件使用二:barManager

    1.拖放控件 2.两种按钮显示形式 1)上面是大图标,下面是说明 a.Add → Largebutton 注:勾选 Show DesignTime enancements 才会出现Add b.添加图片 ...

  7. dl learn task

    https://deeplearning4j.org/cn/word2vec Task 1 分类http://blog.csdn.net/czs1130/article/details/7071734 ...

  8. 【原创】apache虚拟主机配置

    1.取消httpd.conf中以下行的注释 Include conf/extra/httpd-vhosts.conf 2.增加虚拟主机的目录,代表该目录作为一个网站的虚拟目录,配置权限,允许访问: & ...

  9. C#生成高清缩略图 (装在自OPEN经验库)

    来源 http://www.open-open.com/lib/view/open1389943861320.html 代码如下实现图片的高清缩略图 /// <summary> /// 为 ...

  10. Dropout 下(关于《Dropout: A Simple way to prevent neural networks from overfitting》)

    先上菜单: 摘要: Deep neural nets with a large number of parameters are very powerful machine learning syst ...