圆形石子合并问题

题目描述

在一个圆形操场的四周摆放着 n堆石子。 现要将石子有次序地合并成一堆。 规定每次选2 堆石子合并成新的一堆,合并的费用为新的一堆石子数。试设计一个算法,计算出将 n堆石子合并成一堆的最小总费用。

输入

输入数据第1行有1个正整数 n(1≤n≤1000),表示有 n堆石子,每次选2堆石子合并。第2行有 n个整数, 分别表示每堆石子的个数(每堆石子的取值范围为[1,1000]) 。

输出

数据输出为一行, 表示对应输入的最小总费用。

样例输入

7
45 13 12 16 9 5 22

样例输出

313

分析:

       石子合并问题有三种版本,第一种是任意两个堆石子合并,第二种是一条直线的相邻的两堆石子合并,这里是第三种,圆形相邻石子合并。

       针对第一种,很明显用哈夫曼树即贪心的方法即可解决。第二种用动态规划的思路。这里讲讲第三种问题的思路:首先就是怎么解决圆形的问题,我们可能会想到化曲为直,那么怎么把圆形问题转化为直线问题?

       假设操场有5堆石子,如图:

       我们假设最小花费的合并方式如下图:

       至此,我们可以看到,我们不必把这些石子连成一个环就可以解决该问题,其实质上就是直线上的石子合并问题。那么接下来的问题就是,哪一种花费最小?我们知道,n堆石子,可以化成n条直线问题,取最小值即可。下面就是对于每一条直线问题,我们如何解决。第一点,我们要明确这条直线是从哪里开始的;第二点,我们要明确直线包括了哪些石子。即起点和长度。针对第二种问题,我们可以用起点到终点来表示,但是对于环形而言,直线就转化为0->4, 1->0, 2->1, 3->2, 4->3,用数组不好表示。对于起点和长度,我们可以用二维数组,两个坐标分别表示起点和长度。比如dp[1][5]表示从编号为1的石堆开始,合并从1开始的5个石堆所需要的最小花费。

       然后就需要找状态转移方程了,这里定义一个函数int getSum(int start, int step)表示从start开始的step个石堆总共的石子数量。方程如下:

       dp[i][n]是指从i起的n堆石子,dp[i][k]是从i起的k堆,k到k+1为间断点,还剩下n - k堆,是从编号为(i + k) % n的石堆开始的,合并这两堆的费用是getSum(i, n)。步长为1时,花费为0。从步长为2开始,对于每条直线,计算dp[i][n]的最小值,直到n == N(总共的堆数)。dp[i][N]的最小值即为所求。

#include<bits/stdc++.h>

using namespace std;

int n;
int stone[1010];
int dp[1010][1010];

int getSum(int start, int step){
	int sum = 0;
	for(int i = 0; i < step; i++)
		sum += stone[(start + i) % n];
	return sum;
}

int main(){
	while(cin >> n){
		for(int i = 0; i < n; i++){
			cin >> stone[i];
			dp[i][1] = 0;
		}
		for(int t = 2; t <= n; t++){
			for(int i = 0; i < n; i++){
				int sum = getSum(i, t);
				dp[i][t] = INT_MAX;
				for(int k = 1; k < t; k++)
					dp[i][t] = min(dp[i][t], dp[i][k] + dp[(i + k) % n][t - k] + sum);
			}
		}
		int ans = dp[0][n];
		for(int i = 1; i < n; i++)
			ans = min(ans, dp[i][n]);
		cout << ans << endl;
	}
	return 0;
}

/*
input:
7
45 13 12 16 9 5 22

output:
313
*/

 

  • 1
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值