DP专练1( [NOIP 2003]加分二叉树 + 太空梯 )

在这里插入图片描述

加分二叉树

题目

在这里插入图片描述

题解

简单讲解前序//中序//后序遍历

其实说白了,这个*序就是根root的遍历顺序
先序就是root–>left–>right
中序就是left–>root–>right
后序就是left–>right–>root

上图:
在这里插入图片描述
那么这个图很完美啊!!有两个儿子的,又有只有左儿子的,还有只有右儿子的
先序:1 2 4 3 5
中序:4 2 1 3 5
后序:4 2 5 3 1
以讲解先序遍历顺序为例:
首先找到根root=1,然后再去搜索左儿子2,这个时候2为新子树的根,
接着搜索2的左儿子4,4是叶子节点回溯,其次搜索2的右儿子,无,回溯
回到最上面搜索1的右儿子3,然后搜索3的左儿子,空,回溯,
最后找到右儿子5,叶子节点,回溯
讲解一个顺序把近几年所学的逻辑顺序词给憋完了

在这里插入图片描述
那么这道题其实就很水,加上数据那么小,直接暴力跑生成树就ok了
对于搜索的这个新子树的范围l,r,枚举这之间任何一个i为根的情况,
求个MAX
记录个root=i,最后用递归输出即可

代码实现

#include <cstdio>
#define MAXN 35
int dp[MAXN][MAXN];
int root[MAXN][MAXN];
int a[MAXN];
int n; 
void build ( int l, int r ) {
	if ( dp[l][r] ) return;
	if ( l > r ) {
		dp[l][r] = 1;
		return;
	}
	if ( l == r ) {
		dp[l][r] = a[l];
		root[l][r] = l;
		return;
	}
	for ( int i = l;i <= r;i ++ ) {
		build ( l, i - 1 );
		build ( i + 1, r );
		if ( dp[l][i - 1] * dp[i + 1][r] + a[i] > dp[l][r] ) {
			dp[l][r] = dp[l][i - 1] * dp[i + 1][r] + a[i];
			root[l][r] = i;
		}
	}
}
void print ( int l, int r ) {
	if ( ! root[l][r] ) return;
	printf ( "%d ", root[l][r] );
	print ( l, root[l][r] - 1 );
	print ( root[l][r] + 1, r );
}
int main() {
	scanf ( "%d", &n );
	for ( int i = 1;i <= n;i ++ )	
		scanf ( "%d", &a[i] );
	build ( 1, n );
	printf ( "%d\n", dp[1][n] );
	print ( 1, n );
	return 0;
} 

太空梯

题目

题目描述
有一群牛要上太空。他们计划建一个太空梯-----用一些石头垒。他们有k(1<=k<=400)种不同类型的石头,每一种石头的高度为h_i(1<=h_i<=100),数量为c_i(1<=c_i<=10),并且由于会受到太空辐射,每一种石头不能超过这种石头的最大建造高度a_i(1<=a_i<=40000)。帮助这群牛建造一个最高的太空梯。

输入格式
第一行为一个整数即k。第2行到第k+1行每一行有3个数,代表每种类型魔法石的特征,即高度h,限制高度a和数量c。
输出格式
一个整数,即修建太空梯的最大高度。

样例
样例输入
3
7 40 3
5 23 8
2 52 6
样例输出
48
【样例说明】 15+21+12
最底下为3块石头2型,中间为3块石头1型,上面为6块石头3型。放置4块石头2型和3块石头1型是不可以的,因为顶端的石头1型的高度超过了40的限制。

题解

这道题也是一个多重背包水题
在这里插入图片描述
首先肯定会想到排序,因为石头类型的高度限制越小,肯定越往下排,往上很容易超过最高限制

然后我们就定义一个:bool类型的dp[i][j]
表示只使用1~i种类型石头且总高度为j的剋行性,
可以凑出来并且合法就是1,反之0

dp[i][j]=max(dp[i-1][j],dp[i-1][j-k*stone[i].h]) k表示i石头使用的个数

我们知道dp[i][j]只与i-1上一种石头类型有关
所以我们就简化成一维

dp[j]=max(dp[j], dp[j-k*stone[i].h])

这里还是像其他dp题一样,j要从大到小枚举
简单解释为什么;
当我们i++后要重新开始枚举j,如果是从小到大,
因为我们是一维,就会对上一次i改变后的答案进行更改
而我们到后面的会有 j - k * stone[i].h 的操作,这个时候我们其实是需要i-1的状态,
如果从小到大状态答案就被覆盖了,最后ans也会出错

代码实现

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
#define MAXN 405
#define MAX 40005
struct node {
	int h, limit, c;
}stone[MAXN];
bool cmp ( node x, node y ) {
	return x.limit < y.limit;
}
int n, Max;
bool dp[MAX];
int main() {
	scanf ( "%d", &n );
	for ( int i = 1;i <= n;i ++ ) {
		scanf ( "%d %d %d", &stone[i].h, &stone[i].limit, &stone[i].c );
		Max = max ( Max, stone[i].limit );
	}
	sort ( stone + 1, stone + n + 1, cmp );
	dp[0] = 1;
	for ( int i = 1;i <= n;i ++ )
		for ( int j = Max;j >= 0;j -- )
			for ( int k = 0;k <= stone[i].c;k ++ ) {
				if ( j < k * stone[i].h || j > stone[i].limit ) break;
				else dp[j] = max ( dp[j], dp[j - k * stone[i].h] );
			}
	for ( int i = Max;i >= 0;i -- )
		if ( dp[i] ) {
		printf ( "%d\n", i );
		break;
	}
	return 0;
}

好了,就到这里吧,这只是练练手,不然怎么叫专练1呢~~
在这里插入图片描述
等会儿我就会携带着一堆毒瘤来见大家的,bye~
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值