分离与合体题解(区间DP)

题目:

经过在机房里数日的切磁,LYD从社神牛那里学会了分离与合体,出关前,杜神牛给了他一个测试

杜神牛造了个区域,它们紧邻着排成了一行,编号1-n。在这經个区域里都放着一把OI界
的金钥匙,每一把都有一定的价值,LYD当然想得到它们了。然而杜神牛规定LYD不可以一下子
把它们全部拿走,而是每次只可以拿一把。为了尽快地拿到所有的金钥匙,LYD自然就用上了刚学的分离与合体特技。

开始LYD可以选择从 1 ~ n - 1 1~n-1 1n1的任何一个区域(记为K)进入,进入后LYD会在K区域发
生分离,从而分离为两个小LYD。分离完成的同时会有一面墙在k和k+1区域之间升起,从而把
的区间内任选除了区间末尾区域以外(即 1 ~ k ー 1 1~kー1 1k1 k + 1 ~ n ー 1 k+1~nー1 k1n1)的任何一个区域再次发生分离
就一共有了4个小小LYD…重复进行以上所叙述的分离,直到每个小LYD发现自己所在的区间只剩下了一个区域,他们就可以抱起自己梦寐以求的Oi金钥匙。

但是LYD不能就这么分成n多个个体存在于世界上,这些小LYD还会再合体,合体的两个小
LYD所在的区间中间的墙会消失。合体会获得一定的价值,计算方法是:(合并后所在区间的左右
端区域里金钥匙的价值之和)乘(之前分离的时候所在区域的金钥匙价值)
例如,LYD曾经在 1 ~ 3 1~3 13区间中的2号区域分离成为 1 ~ 2 1~2 12和3两个区间,合并时获得的价值就
是:(1号金钥匙价值+3号价值)*(2号金钥匙价值)
LYD请你编程求出最终可以获得的总价值最大是多少。并按照分离阶段从前到后,区域从左
向右的顺序,输出发生分离的区域编号
例如,先打印1分为2的分离区域,然后从左到右打印2分为4的分离区域,然后是4分为

注意:若有多种方案,选择分离区域尽量靠左的方案(也可以理解为输出字典序最小的)

输入

第一行一个正整数n(2≤n≤300)
第二行为n个用空格分开的正整数,表示 1 ~ n 1~n 1n区域里每把金钥匙的价值。
保证运算过程及结果不超出int范围

输出

第一行的一个数,即获得的最大价值。。

第二行按照分离阶段从前到后,区域从左向右的顺序,输出发生分离的区域编号,中间用一个空格隔开,若有多种方案,选择分离区域尽量靠左的方案(也可以理解为输出字典序最小的)

样例输入:

7
1 2 3 4 5 6 7

样例输入:

238
1 2 3 4 5 6

范围:

对于 20 20% 20 的数据 , n ≤ 10 ,n≤10 ,n10

对于 40 40% 40 的数据, n ≤ 50 n≤50 n50

对于 100 100% 100 的数据, n , a i ≤ 300 n,ai≤300 n,ai300

i ≤ 300 i≤300 i300,保证运算过程和结果不超过 32 32 32位正整数范围。

题解:

首先这道题不难发现它就是一道裸题,首先设dp[i][j]为i~j区间的最大价值,那么不难知道dp[i][j]是由元区间dp[i][k] + dp[k + 1][r]的和加上(a[i] + a[y])*a[k],注意区间长度应该是从2开始枚举,因为1时,已经不用分裂了。

难点:

但是我们应如何输出路径呢?首先需要定义一个l[i][j],表示i~j之间最大的k点(集中转点),于是我们就只需要按顺序枚举分裂后的起始位置,结束位置即可得到最终答案,这里可以用dfs枚举,详细见代码:

void _printf(int qi, int zhong, int  y) {
	if(y == 0) {//如果达到目标深度则返回
		return;
	}
	if(l[qi][zhong] == -1){//输出过就跳过
		return;
	}
	if(flag[qi][zhong] == 0) {//如果此时的k没输出过则输出
		flag[qi][zhong] = 1;
		printf("%d ", l[qi][zhong]);
	}
	_printf(qi, l[qi][zhong], y - 1);//枚举划分点左边,这层枚举完了,深度-1
	_printf(l[qi][zhong] + 1, zhong, y - 1);//枚举划分点右边
}

代码:

#include 
#include 
#include 
#include 
using namespace std;
int n, a[305], dp[305][305], l[305][305], sum = 0, flag[305][305];
void _printf(int qi, int zhong, int  y) {
	if(y == 0) {
		return;
	}
	if(l[qi][zhong] == -1){
		return;
	}
	if(flag[qi][zhong] == 0) {
		flag[qi][zhong] = 1;
		printf("%d ", l[qi][zhong]);
	}
	_printf(qi, l[qi][zhong], y - 1);
	_printf(l[qi][zhong] + 1, zhong, y - 1);
}
int main() {
//	freopen("separation.in", "r", stdin);
//	freopen("separation.out", "w", stdout); 
	scanf("%d", &n);
	for(int i = 1;i <= n; i++) {
		scanf("%d", &a[i]);
	}
	memset(l, -1, sizeof(l));
	for(int len = 2;len <= n; len++) {
		for(int i = 1;i <= n - len + 1; i++) {
			int r = i + len - 1;
			for(int k = i;k < r; k++) {
				if(dp[i][k] + dp[k + 1][r] + (a[i] + a[r]) * a[k] > dp[i][r]) {
					dp[i][r] = dp[i][k] + dp[k + 1][r] + (a[i] + a[r]) * a[k];
					l[i][r] = k;
				}
			}
		}
	}
	printf("%d\n", dp[1][n]);
	for(int i = 1;i <= n; i++) {
		_printf(1, n, i);
	}
	return 0;
}

你可能感兴趣的:(动态规划,区间DP)