• 洛谷P1040 加分二叉树


    @

    题目

    https://www.luogu.com.cn/problem/P1040
    在这里插入图片描述

    分析

    这题被分到树形DP的题单上,但是个人感觉更像区间DP+上树的遍历.
    看透了这题的情况下这题应该不难,关键就在“中序遍历为(1,2,3,...n)”,这就意味着一段连续的区间[l,r],以root为根节点,那么左子树就由[l,root-1]组成,右子树由[root+1,r]组成(联想下二叉查找树),这样,状态转移方程就很好推了。
    设f[i][j]表示以区间[l,r]为一棵(子)树的能达到的最大加分,我们就可以枚举这棵树的根节点k,则有:

    [f[i][j]=max {^{f[i][k-1]*f[k+1][j]+score[k] (i<k<j)(以中间点为根)}_{max{^{f[i+1][j] + score[i]}_{f[i][j-1]+sorce[j]}(以左右端点为根)} ]

    特别地,当i=j时f[i][j]=socre[i] (题目有讲)
    以区间长度为状态,区间起点为阶段,这不就是区间DP吗?
    ================== 分割线 ==================
    一轮DP下来,我们就知道了二叉树的最高加分,接下来就是遍历了:
    DP的状态转移中,我们枚举(子)树的根节点,要是我们把它记录下来,那不就能输出前序遍历了吗?
    设root[i][j]为区间[l,r]产生最高加分的根节点,并在状态转移的时候记录,根据root数组,就能写出DFS求前序遍历(注意边界等细节):

    void dfs(int l , int r){//前序遍历
    	if(l > r)return;
    	if(l == r){
    		cout << l << ' ';
    		return;
    	}
    	int k = root[l][r];
    	cout << k << ' ';
    	
    	dfs(l , k - 1);
    	dfs(k + 1 , r);
    }
    

    难道就这么轻松愉快地AC了?
    NONONO,还有一个小细节:字典序输出(题目没有明说)
    这就要求我们在写DP的时候注意一下枚举k点的顺序(要严格按字典序枚举)

    			f[j][r] = f[j + 1][r] + score[j] , root[j][r] = j;
    			for(int k = j + 1 ; k < r ; k++){
    				if(f[j][r] < f[j][k - 1] * f[k + 1][r] + score[k])
    					f[j][r] = f[j][k - 1] * f[k + 1][r] + score[k] , root[j][r] = k;
    			}
    			if(f[j][r] < f[j][r - 1] + score[r])
    				f[j][r] = f[j][r - 1] + score[r] , root[j][r] = r;
    

    即:先枚举k为左端点的情况,再中间,最后右端点(千万不要贪方便把左右端点合并了我一开始就是这样)
    然后:
    真·AC
    撒花

    代码

    #include <iostream>
    #include <cstdio>
    using namespace std;
    int n;
    int score[50];
    int f[50][50];
    int root[50][50];
    void dfs(int l , int r){//前序遍历
    	if(l > r)return;
    	if(l == r){
    		cout << l << ' ';
    		return;
    	}
    	int k = root[l][r];
    	cout << k << ' ';
    	
    	dfs(l , k - 1);
    	dfs(k + 1 , r);
    }
    int main(){
    	cin >> n;
    	for(int i = 1 ; i <= n ; i++)
    		cin >> score[i];
    		//求最大分数
    	for(int i = 1 ; i <= n ; i++)
    		f[i][i] = score[i] , root[i][i] = i;
    		
    	for(int i = 2 ; i <= n ; i++)
    		for(int j = 1 ; j <= n ; j++){
    			int r = i + j - 1;
    			
    			f[j][r] = f[j + 1][r] + score[j] , root[j][r] = j;
    			for(int k = j + 1 ; k < r ; k++){
    				if(f[j][r] < f[j][k - 1] * f[k + 1][r] + score[k])
    					f[j][r] = f[j][k - 1] * f[k + 1][r] + score[k] , root[j][r] = k;
    			}
    			if(f[j][r] < f[j][r - 1] + score[r])
    				f[j][r] = f[j][r - 1] + score[r] , root[j][r] = r;
    		}
    	cout << f[1][n] << endl;
    	dfs(1 , n);
    	cout << endl;
    	return 0;
    } 
    
  • 相关阅读:
    将Color的格式转变成颜色值
    F# 学习笔记 1 基础学习
    GridView 72般绝技
    前台直接读取Web.config中的值的方法
    根据属性名称 获取对象的属性值
    字符集与字符编码简介(转)
    一个WinForm程序配置信息的简单模型和维护工具——设计说明
    扩展DLL调用扩展DLL的LINK2001错误的解决办法之一
    CProfile : 读写私有INI配置文件
    日记:如何在MFC中使用Winsock2
  • 原文地址:https://www.cnblogs.com/dream1024/p/13957540.html
Copyright © 2020-2023  润新知