(C++)石子合并问题:最大/小代价(动态规划,前缀和优化,最详尽代码注释)

写在所有的前面:

本文采用C++实现代码

题目说明

题目

题目出处

《算法设计与分析》课上随堂测验

题目描述Description

设有 n 堆石子排成一排,其编号为 1,2,3,…,n。
每堆石子有一定的质量,可以用一个整数来描述,现在要将这 n 堆石子合并成为一堆。
每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。
找出使总代价最小/最大的方法,输出最小/最大代价。

输入Input

第一行:一个整数 n,代表石子堆数
第二行:n 个整数,代表每堆石子质量

输出Output

第一行:一个整数,代表最小价值
第二行:一个整数,代表最大价值

样例Sample

输入:

4
4 4 5 9

输出:

43
54

限制Hint

解答说明

方案1:动态规划

解题思路

类似于矩阵连乘:本人写的《矩阵连乘(动态规划)(C/C++)最详尽代码注释》
https://blog.csdn.net/just_do_it_sq/article/details/142218288?spm=1001.2014.3001.5501
寻找最佳分隔点(最后一个合并的位置)
从连续 2 堆合并开始,不断增大到所有石子合并
从 i 到 j 开始遍历设分隔点 k ,取最优解
用前缀和队列优化多个数值的合并值计算

一般情况

最后分隔点为 k 时,最后一次合并后,合并总值为:左边总代价 + 右边总代价 + 总质量

特殊情况

一堆石子无法合并,mx[i][i] = mn[i][i] = 0

代码实现

#include<iostream>
using namespace std;

const int N = 100;//常量 N

int n;//n堆石子
int a[N], s[N];//石子堆数组,前缀和数组(前 i 项和)
int mx[N][N], mn[N][N];//最大值,最小值,mx[i][j]从 i 到 j 之间的石子堆合并

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++)
	{
		scanf("%d", &a[i]);//石子堆初始化
		s[i] = s[i - 1] + a[i];//前缀和初始化
	}

	memset(mn, 0x3f, sizeof mn);//给最小值数组 mn 初始化赋值,赋值结果为 0x3f 即无穷大,赋值长度为 mn 的大小
	for (int i = 1; i <= n; i++)mn[i][i] = 0;//只有一堆石子时,合并值为 0

	//探查最佳分隔点
	for (int r = 2; r <= n; r++)// r 堆石子合并
	{
		for (int i = 1; i <= n - r + 1; i++)//从 i 开始
		{
			int j = i + r - 1;//到 j 结束
			for (int k = i; k < j; k++)// k 为分隔点,即 i 到 j 中最后一次合并点,左边合并值 + 右边合并值 + 合并后一整堆提供的合并值
			{
				mx[i][j] = max(mx[i][j], mx[i][k] + mx[k + 1][j] + s[j] - s[i - 1]);//记录最大值
				mn[i][j] = min(mn[i][j], mn[i][k] + mn[k + 1][j] + s[j] - s[i - 1]);//记录最小值
			}
		}
	}

	//输出即可
	printf("%d\n%d", mn[1][n], mx[1][n]);
	return 0;
}

其他解释

最详尽代码注释!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值