• 博客园logo
  • 会员
  • 众包
  • 新闻
  • 博问
  • 闪存
  • 赞助商
  • HarmonyOS
  • Chat2DB
    • 搜索
      所有博客
    • 搜索
      当前博客
  • 写随笔 我的博客 短消息 简洁模式
    用户头像
    我的博客 我的园子 账号设置 会员中心 简洁模式 ... 退出登录
    注册 登录
jacklee404
Never Stop!
博客园    首页    新随笔    联系   管理    订阅  订阅
Acwing-282. 石子合并 区间DP例题

题目

设有 NN 堆石子排成一排,其编号为 1,2,3,…,N1,2,3,…,N。

每堆石子有一定的质量,可以用一个整数来描述,现在要将这 NN 堆石子合并成为一堆。

每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻,合并时由于选择的顺序不同,合并的总代价也不相同。

例如有 44 堆石子分别为 1 3 5 2, 我们可以先合并 1、21、2 堆,代价为 44,得到 4 5 2, 又合并 1、21、2 堆,代价为 99,得到 9 2 ,再合并得到 1111,总代价为 4+9+11=244+9+11=24;

如果第二步是先合并 2、32、3 堆,则代价为 77,得到 4 7,最后一次合并代价为 1111,总代价为 4+7+11=224+7+11=22。

问题是:找出一种合理的方法,使总的代价最小,输出最小代价。

输入格式

第一行一个数 NN 表示石子的堆数 NN。

第二行 NN 个数,表示每堆石子的质量(均不超过 10001000)。

输出格式

输出一个整数,表示最小代价。

数据范围

1≤N≤3001≤N≤300

输入样例:

4
1 3 5 2

输出样例:

22

思路 区间dp

​ 我们只考虑两个区间的合并,将左右区间的最优解合并保证了最终解的最优性,以分界点来推导状态,则长度为\(len\)的区间可以划分\(len - 1\)个分界点,设\(dp[i][j]\)表示左端点为\(l\), 右端点为\(r\)的合并所需最小代价,\(f[i][j]\)的状态可以由若干个分界点划分的区间得到:

\(\quad f[i][j] = min(f[i][i + k - 1] + f[i + k][j] + \sum \limits^j_{x=i}a_x)\)

所有状态: \(O(n^2)\)

状态计算: \(O(n)\), 对于单个状态而言,枚举所有分界点

复杂度: \(O(n^3)\)

Code

#include <iostream>

const int N = 350;

int a[N], prefix[N], dp[N][N];

int main() {
    int n;
    std::cin >> n;
    for(int i = 1; i <= n; i ++) std::cin >> a[i], prefix[i] = a[i] + prefix[i - 1];
    
    // dp[l][k - 1], dp[k + 1][r]
    // dp[l][r], dp[r + 1][r]
    // dp[l][r - 1], dp[r][r]
    for(int len = 2; len <= n; len ++) {
        for(int l = 1; l + len - 1 <= n; l ++) {
            int r = l + len - 1;
            dp[l][r] = 1e9;
            for(int k = l; k <= r; k ++) {
                dp[l][r] = std::min(dp[l][r], dp[l][k - 1] + dp[k][r] + prefix[r] - prefix[l - 1]);
            }
        }
    }
    std::cout << dp[1][n];
}
posted on 2023-01-14 13:21  Jack404  阅读(21)  评论(0)    收藏  举报
刷新页面返回顶部
博客园  ©  2004-2025
浙公网安备 33010602011771号 浙ICP备2021040463号-3