• 动态规划-区间dp


    石子合并

    问题描述

    设有N堆石子排成一排,其编号为(1,2,3,…,N)
    每堆石子有一定的质量,可以用一个整数来描述,现在要将这(N)堆石子合并成为一堆。
    每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。
    例如有4堆石子分别为 (1 3 5 2), 我们可以先合并(1、2)堆,代价为(4),得到(4 5 2), 又合并(1,2)堆,代价为(9),得到(9 2) ,再合并得到(11),总代价为(4+9+11=24)
    如果第二步是先合并(2,3)堆,则代价为(7),得到(4 7),最后一次合并代价为(11),总代价为(4+7+11=22)
    问题是:找出一种合理的方法,使总的代价最小,输出最小代价。

    问题分析

    问题分析的关键在于理解到:无论多少堆石子,最后一步一定是两堆合并为一堆
    所以将第(i)堆石子到第(j)堆石子进行合并可以按照分界点进行划分
    下图中的(1)(k-1)表示分界点左侧石子的堆数,其中(k)表示从第(i)堆石子到第(j)堆石子总共的堆数

    代码实现

    #include <iostream>
    #include <algorithm>
    #include <cstring>
    
    using namespace std;
    
    const int N = 310, INF = 0x3f3f3f3f;
    
    int n;
    int a[N];
    int f[N][N];
    
    int main()
    {
        cin >> n;
        for (int i = 1; i <= n; ++ i) 
            {
                cin >> a[i];
                a[i] += a[i - 1];
            }
    
        // 长度为1的序列合并代价均为0,所以长度从2开始
        for (int len = 2; len <= n; ++ len)
            for (int i = 1; i + len - 1 <= n; ++ i)
            {
                int l = i, r = i + len - 1;
                f[l][r] = INF; // 如果使用memset(f, 0x3f, sizeof f),那么就需要初始化长度为1的序列的初始值了
                for (int k = l; k < r; ++ k)
                    f[l][r] = min(f[l][r], f[l][k] + f[k + 1][r] + a[r] - a[l - 1]);
            }
            
        cout << f[1][n] << endl;
        
        return 0;
    }
    
  • 相关阅读:
    [Contest on 2020.4.2] 影帝杯狂欢赛
    [BZOJ 3821] 玄学
    CodeForces 432D Prefixes and Suffixes
    CodeForces 17E Palisection
    CodeForces 665E Beautiful Subarrays
    BZOJ 2989 数列
    changeeksdja
    Jmeter学习——1
    LoadRunner监控Linux与Windows方法(经典)
    LR检查点小结
  • 原文地址:https://www.cnblogs.com/G-H-Y/p/14439344.html
Copyright © 2020-2023  润新知