求解石子合并问题(选择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循环的顺序)

总结:

要按长度递增来dp,不能按位置dp

上一篇:Python实现高精度(一)加法计算


下一篇:KMP算法