P2024 二叉苹果树
此题为洛谷上P2024 二叉苹果树详解,核心思想为DP与dfs,其中DP方程转移方程为:
\[dp[x][i] = max(dp[x][i], dp[l][j] + dp[r][i-j])
\]
解析见代码。
代码如下:
//dp[i][几]以第i个点为根的子树内砍掉了几根树枝最多苹果数
//ans = dp[1][n-1-q]
//dp[i][j], dp[x][2]+dp[y][j-2]
#include<bits/stdc++.h>
using namespace std;
int n, q, tu[105][105], a, b, c, dp[105][105], size[105];
//n为总结点数,q为应该保留的树枝数;
//size数组用于累计树的大小,即拥有几个儿子点;
void dfs(int x, int y){
size[x] = 1;//在根结点时树的大小为1;
int l = 0, r = 0;//定义每个节点的左右子树并初始化为0;
for(int i = 1; i <= n; i++) if(tu[x][i] != -1 && i != y){
dfs(i, x);//让dfs继续往下搜索,并且防止儿子搜回父亲点以至于反复横跳;
size[x] += size[i];//计算树的大小
if(l) r = i;
else l = i;//对左右子树进行初始化,原理:
}
if(!r){
dp[x][0] = tu[y][x];//?
}
else{
for(int i = 0; i <= min(size[x], q); i++){//条件判断句防止删边数超过可删边数
for(int j = 0; j <= i; j++) if(j<=size[l] && i-j<=size[r]){
dp[x][i] = max(dp[x][i], dp[l][j] + dp[r][i-j]);
//状态方程,即最优解应取左右子树可砍掉的最多苹果数与历史最优解的最优值(太优了)
}
if(i < size[x] && x != 1) dp[x][i] += tu[y][x];
//累计 ?
}
}
}
int main(){
scanf("%d%d", &n, &q), q = n - 1 - q;
//读入,q被重新定义为应被减掉的树枝数,又因为根节点没有父亲,故表达为n-1-n;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) tu[i][j] = -1;//这里把整个树的权值都初始化为-1;
for(int i = 1; i < n; i++)
scanf("%d%d%d", &a, &b, &c), tu[a][b] = tu[b][a] = c;//给树枝赋权值,即存图;
dfs(1, 1);//由于数据较小,所以搜索遍历;
printf("%d", dp[1][q]);//输出答案
return 0;
}
搞定。