求解石子合并问题(选择dp循环的顺序)
作者:互联网
实验目的:
练习动态规划
实验内容:
有n堆石子排成一排,每堆石子有一定的数量,现要将n堆石子合并成为一堆,合并只能每次将相邻的两堆石子堆成一堆,每次合并花费的代价为这两堆石子的和, 经过n-1次合并后成为一堆,求出总代价的最小值。
输入描述:有多组测试数据,输入到文件结束。每组测试数据的第1行有一个整数n, 表示有n堆石子,接下来的一行有n(0<n<200)个数,分别表示这n堆石子的数目,用空格隔开。
输出描述:输出总代价的最小值,占单独的一行。
输入样例:
3
1 2 3
7
13 7 8 16 21 4 18
样例输出:
9
239
思路:
刚开始想用贪心,每次把最小的相邻的两堆合并,但手写连样例都过不去,所以不能用贪心,全局最优要动态规划。
用dp[i][j]表示第i堆到第j堆合并的最优解,
dp[i][j] = min{dp[i][j] , dp[i][k]+dp[k+1][j]+ cost(i,j)}
k在[i,j)之间,
cost是i到j的花费,可以以空间换时间不用每次求,用一维数组存0到i的花费,cost(i,j) = cost(j) - cost(i) + a[i]。
最后dp[0][n-1]即为答案
代码
#include <iostream>
#include <string>
#include <algorithm>
#include <vector>
#define MAXN 205
#define INF 0x3f3f3f3f
using namespace std;
int main()
{
int n;
int a[MAXN];
int cost[MAXN];
while (cin >>n) {
cin >> a[0];
cost[0] = a[0];
for (int i = 1; i < n; i++) {
cin >> a[i];
cost[i] = cost[i - 1] + a[i];
}
vector<vector<int>> dp(n, vector<int>(n, 0)); //建一个n*n的二维数组,初始化为0
for (int i = 0; i < n; i++) {
dp[i][i] = 0;
}
for (int lens = 1; lens < n; lens++) {
for (int i = 0; i < n - lens; i++) {
int j = i + lens;
dp[i][j] = INF;
for (int k = i; k < j; k++) {
dp[i][j] = min(dp[i][j], dp[i][k] + dp[k + 1][j] + cost[j] - cost[i] + a[i]);
}
}
}
cout << dp[0][n - 1];
}
return 0;
}
结果
总结:
要按长度递增来dp,不能按位置dp
标签:求解,int,石子,lens,++,cost,dp 来源: https://blog.csdn.net/lllzzzhhh2589/article/details/121233456