LuoGu P1040 加分二叉树【虚伪的区间DP】

题目链接
LuoGu P1040 加分二叉树【虚伪的区间DP】_第1张图片
自己还是有点害怕树形DP的问题,其是这道题完全是一道区间DP,因为给了中序遍历,所以我们只需要不断的确定在某个区间里选择哪个作为根,然后递归的确定他的左子树选择哪个作为根,右子树选择哪个作为根即可,但是这样自顶向下进行递归肯定会超时,所以我们采取自底向上的区间DP策略:

#define inf 0x3f3f3f3f
#define ll long long
#define vec vector
#define P pair
#define MAX 35

int n, a[MAX];
ll dp[MAX][MAX];//dp[i][j]:i-j区间的最大分数

//len:区间长度, [l,r]:左右端点, 目标值
void pre(int l, int r, ll val) {
	for (int i = l; i <= r; i++) {
		if (dp[l][i - 1] * dp[i + 1][r] + a[i] == val || l == r) {//这是跟
			cout << i << " ";
			pre(l, i - 1, dp[l][i - 1]);
			pre(i + 1, r, dp[i + 1][r]);
			return;
		}
	}
}

int main() {
	cin >> n;
	memset(dp, 0, sizeof(dp));
	for (int i = 1; i <= n; i++) {
		cin >> a[i], dp[i][i] = a[i];
		for (int j = 0; j < i; j++)
			dp[i][j] = 1;//左端点小于右端点,空树,1
	}

	for (int i = 2; i <= n; i++) {//枚举区间长度
		for (int beg = 1; beg <= n - i + 1; beg++) {//枚举起点
			int t = beg + i - 1;
			for (int k = beg; k <= t; k++) {//枚举区间内的所有端点
				dp[beg][t] = max(dp[beg][t], dp[beg][k - 1] * dp[k + 1][t] + a[k]);
			}
		}
	}
	cout << dp[1][n] << endl;
	pre(1, n, dp[1][n]);
}

你可能感兴趣的:(牛客网&LuoGu练习题)