环形石子合并问题

题目描述
在一个圆形操场的四周摆放着n堆石子,现要将石子有次序地合并成一堆。规定每次只能选取相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。编写程序,读入石子堆数n及每堆的石子数(<=20)。选择一种合并石子的方案,使得做n-1次合并,得分的总和最大(小)。
输入样例
4
4 4 5 9
输出样例
43
54

思路
类似于直线上环形石子合并问题,将原数组复制一份放到数组的尾部,可以巧妙地实现了每个石子作为起点的情况。枚举i-j的之间的分裂点k,则花费为f[i][k] + f[k+1][j] + s[j]-s[i-1]。
状态转移方程
f[i][j] = 0 ; i==j
f[i][j] = min(f[i][j],f[i][k] + f[k+1][j] + s[j]-s[i-1]) i<j
f[i][j]:从第i堆石子到第j堆石子合并起来的最小花费,k为分裂点
为计算方便,维护了一个s[]前缀和数组。

代码

#include<iostream>
using namespace std;
const int N = 1e3 + 10;
int a[N];
int f[N*2][N*2];
int g[N*2][N*2];
int sum[N*2];
int n;
int minans = 0x3f3f3f3f;
int maxans = 1 - 0x3f3f3f3f;
int main()
{
	cin>>n;
	for(int i = 1; i <= n; i++ )
	{
		cin>>a[i];
		a[i+n] = a[i]; 
	}
	for(int i = 1; i <= 2*n; i++)
	    sum[i]=sum[i - 1] + a[i];
	    
	for(int len = 2; len <= n; len++)
	{
		for(int i = 1; i + len -1 <= 2 * n; i++)
		{
			int j = len + i -1;
			f[i][j] = 0x3f3f3f3f;
			g[i][j] = 1 - 0x3f3f3f3f; 
			for(int k = i; k < j; k++)
			{
				f[i][j] = min(f[i][j], f[i][k] + f[k + 1][j] + sum[j] - sum[i - 1]);
				g[i][j] = max(g[i][j], g[i][k] + g[k + 1][j] + sum[j] - sum[i - 1]); 
			}
		}
	}

	for(int i = 1; i <= n; i++)
	{
		minans = min(minans,f[i][i + n - 1]);
		maxans = max(maxans,g[i][i + n - 1]);
	}
	cout<<minans<<endl;
	cout<<maxans<<endl;
	return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这是一个经典的动态规划问题,可以使用C语言实现。以下是实现代码及注释: ```c #include <stdio.h> #include <stdlib.h> #include <limits.h> // 定义石子数的最大值 #define MAX_N 100 // 定义动态规划数组的最大值 #define MAX_DP_ARRAY_SIZE MAX_N * MAX_N // 定义最小值和最大值的常量 #define MIN_SCORE INT_MIN #define MAX_SCORE INT_MAX // 定义动态规划数组 int dp[MAX_DP_ARRAY_SIZE]; // 计算石子的总数 int calcTotal(int piles[], int n) { int total = 0; for (int i = 0; i < n; i++) { total += piles[i]; } return total; } // 计算最小得分 int calcMinScore(int piles[], int n) { // 初始化动态规划数组 for (int i = 0; i < MAX_DP_ARRAY_SIZE; i++) { dp[i] = MAX_SCORE; } // 计算石子的总数 int total = calcTotal(piles, n); // 填充动态规划数组 for (int len = 1; len <= n; len++) { for (int i = 0; i + len - 1 < n; i++) { int j = i + len - 1; if (len == 1) { dp[i * n + j] = 0; } else { for (int k = i; k < j; k++) { dp[i * n + j] = fmin(dp[i * n + j], dp[i * n + k] + dp[(k + 1) * n + j] + total); } } } } // 返回最小得分 return dp[n - 1]; } // 计算最大得分 int calcMaxScore(int piles[], int n) { // 初始化动态规划数组 for (int i = 0; i < MAX_DP_ARRAY_SIZE; i++) { dp[i] = MIN_SCORE; } // 计算石子的总数 int total = calcTotal(piles, n); // 填充动态规划数组 for (int len = 1; len <= n; len++) { for (int i = 0; i + len - 1 < n; i++) { int j = i + len - 1; if (len == 1) { dp[i * n + j] = 0; } else { for (int k = i; k < j; k++) { dp[i * n + j] = fmax(dp[i * n + j], dp[i * n + k] + dp[(k + 1) * n + j] + total); } } } } // 返回最大得分 return dp[n - 1]; } int main() { // 读入石子数 int n; scanf("%d", &n); // 读入每石子的数量 int piles[MAX_N]; for (int i = 0; i < n; i++) { scanf("%d", &piles[i]); } // 计算最小得分和最大得分 int minScore = calcMinScore(piles, n); int maxScore = calcMaxScore(piles, n); // 输出结果 printf("最小得分:%d\n", minScore); printf("最大得分:%d\n", maxScore); return 0; } ``` 该算法的时间复杂度为 $O(n^3)$,空间复杂度为 $O(n^2)$,其中 $n$ 表示石子的数量。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值