石子合并 动态规划(环形)

1、问题描述:问题来源 NWPU noj 1148

在一个圆形操场的四周摆放着n堆石子(n<= 100),现要将石子有次序地合并成一堆。规定每次只能选取相邻的两堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。编一程序,读入石子堆数n及每堆的石子数(<=20)。选择一种合并石子的方案,使得做n-1次合并,得分的总和最小; 比如有4堆石子:44 5 9 则最佳合并方案如下:
4 4 5 9 score: 0
8 5 9 score: 8
13 9 score: 8 + 13 = 21
22 score: 8 + 13 + 22 = 43


2、输入

可能有多组测试数据。 当输入n=0时结束! 第一行为石子堆数n(1<=n<=100);第二行为n堆的石子每堆的石子数,每两个数之间用一个空格分隔。


3、输出

合并的最小得分,每个结果一行。


4、问题解析

这个问题和直线型的区别在于最后一堆和第一堆也是相邻的,可以把圆形转换成直线型,把问题扩展为2n-1堆石子,举个例子,如果环形石子堆是4 4 5 9,那么转换成直线型就变成了 4 4 5 9 4 4 5,所以最终就不是计算  0~n-1了,而是在 0~n-1,1-n,2-n+1,…,n-1~2n-2中选择最小的。计算方法和直线型的相同。


5、代码如下:(已经提交过验证)

#include<iostream>
#include<vector>

using namespace std;

int main() {

	int n;//n堆石子
	int i, j, k, sum, tmp;//辅助变量
	while (cin >> n) {
		if (0 == n)break;
		sum = 0;
		tmp = INT_MAX;
		vector<int> arr(2*n-1);
		vector<vector<int> >min(2*n-1, vector<int>(2*n-1));//min[i][j]代表合并第i堆到第j堆的最小得分
		for (i = 0; i < n; ++i) {
			cin >> arr[i];
		}
		for (i = n; i <= 2*n - 2; ++i) {
			arr[i] = arr[i - n];
		}
		//给min赋值
		for (i = 0; i <= 2*n-2; ++i) {
			min[i][i] = 0;//初始化为0
		}
		for (i = 2*n - 3; i >= 0; --i) {
			for (j = i + 1; j <= 2*n-2; ++j) {
				for (k = i; k <= j; ++k) {
						sum += arr[k];
					
					//	cout << "sum的值:" << sum << endl;
				}//end for k
				for (k = i; k < j; ++k) {
					if (tmp > min[i][k] + min[k + 1][j]) {
						tmp = min[i][k] + min[k + 1][j];
					}
				}//end for k
				min[i][j] = sum + tmp;
				sum = 0;
				tmp = INT_MAX;
			}// end for j
		}// end for i
		//选出长度为n的最小的值
		int Min = INT_MAX;
		for (i = 0; i < n; ++i) {
			if (min[i][i + n - 1] < Min)
				Min = min[i][i + n - 1];
		}
		cout <<Min << endl;
		Min = INT_MAX;
	}
}
    原文作者:动态规划
    原文地址: https://blog.csdn.net/qq_34266990/article/details/78710044
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞