首页 > 技术文章 > P2024 二叉苹果树

xuzhengyang 2021-02-20 14:19 原文

此题为洛谷上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;
}


搞定。

推荐阅读