动态规划之石子合并

    xiaoxiao2025-08-17  1

    这类问题是动态规划中经典题型,主要有三种变形。网上关于这个问题的资料很多,我就不赘述了。简单的介绍一下这三种题型。

    有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动任意的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。 这类问题使用贪心算法即可解决,哈夫曼树的变形。每次只要合并最小的两堆,即可保证结果最小(最大)。

    链式结构:有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。 这个问题需要使用动态规划求解,如果使用贪心算法求得当前最小值可能不是最优解。如: 7 6 5 7 100 如果使用贪心算法,合并思路为6+5 =11;7+11 =18;18+7 = 25;25+100 = 125。正确的最优解应该是通过动态规划求得:7+6 = 13;5+7 = 12;13+ 12 = 25; 25+100 = 125; 最后结果应该是13+12+25+125 = 175。

    环形结构:在一个圆形操场的四周有N堆石子,现要将石子有序的合并成一堆,规定如下:每次只能移动相邻的2堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费最小(或最大)。

    - 问题二解法:

    设dp[i][j]表示第i到第j堆石子合并的最优值,sum[i][j]表示第i到第j堆石子的总数量。那么就有状态转移公式:

    #include "stdafx.h" #include <iostream> #include <vector> #include <climits> #include <iomanip> using namespace std; int movebook_list(vector<int> book){ int length = book.size(); if (length == 0) return 0; if (length == 1) return book[0]; int mergeTotal = INT_MAX; int **dp = new int *[length];//保存i->j合并的最优结果 int **s = new int *[length];//保存截断点位置 int **sum = new int *[length];//保存i-j的和 for (int i = 0; i < length; i++){ dp[i] = new int[length]; s[i] = new int[length]; sum[i] = new int[length]; } for (int i = 0; i < length; i++){ dp[i][i] = 0; s[i][i] = i; sum[i][i] = book[i]; for (int j = i + 1; j < length; j++){ sum[i][j] = sum[i][j-1] + book[j]; } } for (int len = 2; len <= length; len++) { int j; for (int i = 0; i < length - len + 1; i++) { j = (i + len - 1) ; dp[i][j] = INT_MAX; for (int k = s[i][j-1]; k<=s[i+1][j]&& (k<j);k++)//利用四边形法则进行优化,添加一个截断的标志位s,后面的求解根据之前截断点继续向后搜索以简化求解过程 { if (dp[i][j]> dp[i][k] + dp[k+1][j] + sum[i][j]){ dp[i][j] = dp[i][k] + dp[k+1][j] + sum[i][j]; s[i][j] = k; } } } } mergeTotal = dp[0][length - 1]; //打印求解所使用的矩阵 for (size_t i = 0; i < length; i++) { int j = 0; for (; j < i; j++) cout << setw(4) << ""; for (; j < length; j++) cout << setw(4) << left << sum[i][j]; cout << endl; } cout << endl; for (size_t i = 0; i < length; i++) { int j = 0; for (; j < i; j++) cout << setw(4) << ""; for (; j < length; j++) cout << setw(4) << left << dp[i][j]; cout << endl ; } cout << endl; for (size_t i = 0; i < length; i++) { int j = 0; for (; j < i; j++) cout << setw(4) << ""; for (; j < length; j++) cout << setw(4) << left << s[i][j]; cout << endl; } cout << endl; for (int i = 0; i < length; i++) { delete[] dp[i]; delete[] sum[i]; delete[] s[i]; } delete[] dp; delete[] sum; delete[] s; return mergeTotal; } int main() { int N = 0; vector <int> b = { 7,6,5,7,100}; while (cin >> N){ cout << movebook_list(b); cout << endl; } return 0; }

    - 问题三解法:

    状态转移公式:

    i<=k<j

    int movebook_circle(vector<int> book){ int length = book.size(); if (length == 0) return 0; if (length == 1) return book[0]; int mergeTotal = INT_MAX; int **dp = new int *[length]; int **s = new int *[length]; int **sum = new int *[length]; for (int i = 0; i < length; i++){ dp[i] = new int[length]; s[i] = new int[length]; sum[i] = new int[length]; } for (int i = 0; i < length; i++){ dp[i][i] = 0; s[i][i] = i; sum[i][i] = book[i]; for (int j = i + 1; j < length + i; j++){ sum[i][j%length] = sum[i][(j - 1) % length] + book[j%length]; } } for (int len = 2; len <= length; len++) { int j; for (int i = 0; i < length; i++) { j = (i + len - 1) % length; dp[i][j] = INT_MAX; for (int k = i; k<i + len - 1; k++) { if (dp[i][j]> dp[i][k%length] + dp[(k + 1) % length][j] + sum[i][j]) dp[i][j] = dp[i][k%length] + dp[(k + 1) % length][j] + sum[i][j]; } } } for (int j = 0; j < length; j++) { if (dp[(j + 1) % length][j] < mergeTotal) mergeTotal = dp[(j + 1) % length][j]; } for (size_t i = 0; i < length; i++) { int j = 0; for (; j < length; j++) cout << setw(4) << left << sum[i][j]; cout << endl; } cout << endl; for (size_t i = 0; i < length; i++) { int j = 0; for (; j < length; j++) cout << setw(4) << left << dp[i][j]; cout << endl; } cout << endl; for (int i = 0; i < length; i++) { delete[] dp[i]; delete[] sum[i]; delete[] s[i]; } delete[] dp; delete[] sum; delete[] s; return mergeTotal; } int main() { int N = 0; vector <int> b = { 7,6,5,7,100}; while (cin >> N){ //cout << movebook_list(b); //cout << endl; cout << movebook_circle(b); cout << endl; } return 0; }
    转载请注明原文地址: https://ju.6miu.com/read-1301824.html
    最新回复(0)