区间dp之石子归并问题讲解(经典例题)

如题

1021 石子归并 
基准时间限制:1 秒 空间限制:131072 KB 分值: 20  难度:3级算法题

N堆石子摆成一条线。现要将石子有次序地合并成一堆。规定每次只能选相邻的2堆石子合并成新的一堆,并将新的一堆石子数记为该次合并的代价。计算将N堆石子合并成一堆的最小代价。

例如: 1 2 3 4,有不少合并方法
1 2 3 4 => 3 3 4(3) => 6 4(9) => 10(19)
1 2 3 4 => 1 5 4(5) => 1 9(14) => 10(24)
1 2 3 4 => 1 2 7(7) => 3 7(10) => 10(20)

括号里面为总代价可以看出,第一种方法的代价最低,现在给出n堆石子的数量,计算最小合并代价。
Input
第1行:N(2 <= N <= 100)
第2 - N + 1:N堆石子的数量(1 <= A[i] <= 10000)
Output
输出最小合并代价
Input示例
4
1
2
3
4
Output示例
19

题意分析:

    已知:石子堆w[1,n],每堆石子数为w[i], i∈[1,n].

    操作:每次合并相邻堆,merge(i, i+1),cost为w[i]+w[i+1], i∈[1,n).

    直到石子都堆到一起,求得MinCost(1,n).


本题首先想到贪心,每次合并相邻最小堆,但是在归并过程中就会发现子问题并不是独立的(不符合贪心思想)。

既然子问题不是独立无关的,那就要考虑用动态规划把大规模问题转为小规模问题,自底向上归并,通过保存子问题状态,优化重叠子问题,不断靠近最优解。


我们假如一共四堆1,2,3,4.    ("|"表示两者相邻 可以合并,  eg:1|23表示:23为一堆,1和23为相邻堆)

    合并2堆时有:minCost(1|2,2|3, 3|4);

    合并3堆时有:minCost(1|23, 12|3, 2|34, 23|4);

    那么最后合并到4时,是minCost(1|234, 12|34, 123|4).

代码

#include 
#include 
#include 
#define INF 0x3f3f3f3f
#define NMAX 105
using namespace std;
int n, x, dp[NMAX][NMAX], sum[NMAX];
int main(){
    while (cin>>n){
        sum[0] = 0;
        memset(dp, INF, sizeof(dp));
        for (int i = 1; i <= n; ++i) {
            cin>>sum[i];
            sum[i] += sum[i-1]; //前缀和
        }
        for(int i=1;i<=n;i++) {
            dp[i][i] = 0;       //区间长为1 无归并
        }
        for (int len = 2; len <= n; ++len) {    //区间长度
            for (int i = 1; i <= n; ++i) {      //区间起点 第i堆
                int r = i+len-1;  //区间终点 第r堆
                if(r > n)break;
                int cost = sum[r] - sum[i-1]; //合并必要代价
                for (int j = i; j < r; ++j) {   //分割线   1 | 2 | 3 | 4
                    dp[i][r] = min(dp[i][r], dp[i][j]+dp[j+1][r] + cost);
                }
            }
        }
        cout<

状态方程中的k就是指分割点

今天就写到这,这是区间动态规划经典题型,非常值得学习。加油!

你可能感兴趣的:(AC,Dreamer,动态规划)