【算法设计与分析】石子合并问题,对于给定n堆石子,计算合并成一堆的最小得分和最大得分。

一、问题描述

  在一个圆形操场的四周摆放着n堆石子。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的石子数记为该次合并的得分。试设计一种算法,计算出将n堆算法合并成一堆的最小得分和最大得分。
数据输入: 由文件input.txt提供输入数据,文件的第1行是正整数n(1≤n≤100),表示有n堆石子。第2行有n个数,分别表示每堆石子的个数。
结果输出: 将计算结果输出到文件output.txt。文件第1行的数是最小得分,第2行中的是最大得分。

二、问题分析

  链式的石子合并问题是相邻两堆之间可以合并,那么环形的和链式的区别就在于环形的相当于是链式的头尾两堆也能合并,那么我们只要解决如何在链式的基础上更换每次头和尾的问题即可,即环形的切割点n堆,有n个切割点,每次以区间长度为n的链式的进行求解。我们创建长度为2*n的数组,存放两次石子序列即可。它的最优子结构和链式一样,把当前的链式区间划分的代价达到最优即可。
  在input.txt文本的第1行输入石子堆数,第2行输入每堆石子的个数,运行程序。
在这里插入图片描述

三、运行结果

  运行程序后,打开output.txt文本,文件第1行的数是最小得分,第2行中的是最大得分。
在这里插入图片描述

四、源代码

#include<iostream>
#include<fstream>
using namespace std;
int f[100][100];
int ff[100][100];
int v[100][100];
int a[100];
int n;
int main()
{
	ifstream in("input.txt");
	ofstream out("output.txt");
	in >> n; //n堆石子
	for (int i = 1; i <= n; i++) //每堆石子的个数
	{
		in >> a[i];
		a[i + n] = a[i];
	}

	for (int i = 1; i <= n * 2; i++)
	{
		for (int j = 1; j <= n * 2; j++)
		{
			if (i != j)
				f[i][j] = 10000;
		}
	}

	for (int i = 1; i <= n * 2; i++)
	{
		for (int j = i; j <= n * 2; j++)
		{
			for (int k = i; k <= j; k++)
			{
				v[i][j] += a[k];
			}
		}
	}

	for (int len = 1; len <= n; len++)//区间划分
		for (int l = 1, r = l + len; l < n * 2 && r < n * 2; l++, r = l + len)
		{
			for (int k = l; k < r; k++)  //选择区间分割点
			{
				f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + v[l][r]);
				ff[l][r] = max(ff[l][r], ff[l][k] + ff[k + 1][r] + v[l][r]);
			}
		}
	int ans1 = 10000, ans2 = 0;
	for (int i = 1; i <= n; i++) ans1 = min(ans1, f[i][i + n - 1]); //计算最小得分
	for (int i = 1; i <= n; i++) ans2 = max(ans2, ff[i][i + n - 1]);//计算最大得分
	out << ans1 << endl;
	out << ans2;
	in.close();
	out.close();
	return 0;
}
  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: 题目描述:在一个圆形操作场的四周摆放着n堆石子。现在要将石子有序地合并一堆。规定每次只能选相邻的两堆石子合并新的一堆。试设计一个算法计算出将n堆石子合并一堆的最小得分和最大得分算法设计:对于给定n堆石子计算合并一堆的最小得分和最大得分。 数据输入: 第1行是整数n,1<=n<=100,表示有n堆石子。第2行有n个数,分别表示n堆石子的个数。 结果输出: 第1行是最小得分,第2行是最大得分。 ### 回答2: 这是一道经典的动态规划问题。我们可以设dp[i][j]表示将第i堆到第j堆石子合并一堆的最小得分和最大得分。 对于最小得分,我们可以先对于每个i,设dp[i][i]=0。因为单独一堆石子不需要合并。接下来我们可以考虑dp[i][j]如何转移。我们可以枚举第k个位置,将第i~j堆分两边,即第i~k堆和第k+1~j堆,然后将这两边分别合并一堆,得到总得分sum,然后再加上将这两边合并的这一堆的石子数,即stones[k]。即dp[i][j]=min(dp[i][j], sum+stones[k]+dp[i][k]+dp[k+1][j])。我们可以从小到大枚举区间长度,即先枚举区间长度为2的情况,然后依次增加。 对于最大得分,我们同样可以对于每个i,设dp[i][i]=0。接下来我们可以考虑dp[i][j]如何转移。我们可以枚举第k个位置,将第i~j堆分两边,即第i~k堆和第k+1~j堆,然后将这两边分别合并一堆,得到总得分sum,然后再加上将这两边合并的这一堆的石子数,即stones[k]。即dp[i][j]=max(dp[i][j], sum+stones[k]).同样我们可以从小到大枚举区间长度,依次增加。 最后我们可以输出dp[1][n]即可得到最小得分和最大得分。时间复杂度为O(n^3)。 ### 回答3: 首先,我们可以定义一个dp数组,其中dp[i][j]表示将第i到第j堆石子合并为一堆所需要的最小得分和最大得分。对于任意的i <= j,初始值应该为0。接下来,我们可以按照以下步骤进行动态规划: 1. 枚举区间长度k,从2到n逐个考虑。 2. 对于固定的k,枚举左端点i,右端点j可以通过i+j=k+1得到。例如,当k=4时,可以用(1,4),(2,3),(3,2),(4,1)四个数对分别代表左端点和右端点。 3. 对于当前枚举的区间[i,j],我们需要枚举中间点p,i <= p < j,将区间[i,j]拆分两个子区间[i,p],[p+1,j]并计算合并得分。此时,每个子区间的值可以通过之前已经算出的dp数组中的值得到。 4. 针对当前的合并方式,计算得分并更新dp数组中的值,同时更新最小得分和最大得分。 5. 最终,dp[1][n]就是将n堆石子合并一堆所需要的最小得分和最大得分。 下面是算法实现的伪代码: input n input a1,a2,...,an initialize dp array with zeros for k from 2 to n do: for i from 1 to n-k+1 do: j = i + k - 1 for p from i to j-1 do: score = dp[i][p] + dp[p+1][j] + sum(a[i...j]) update dp[i][j] with the min and max score output dp[1][n][0] as the min score output dp[1][n][1] as the max score 时间复杂度为O(n^3), 空间复杂度为O(n^2)。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

会举重的薯片

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值