区间dp-洛-石子合并(无环)

P1775 石子合并(弱化版)

题目描述

设有 \(N(N \le 300)\) 堆石子排成一排,其编号为 \(1,2,3,\cdots,N\)。每堆石子有一定的质量 \(m_i\ (m_i \le 1000)\)。现在要将这 \(N\) 堆石子合并成为一堆。每次只能合并相邻的两堆,合并的代价为这两堆石子的质量之和,合并后与这两堆石子相邻的石子将和新堆相邻。合并时由于选择的顺序不同,合并的总代价也不相同。试找出一种合理的方法,使总的代价最小,并输出最小代价。

输入格式

第一行,一个整数 \(N\)。

第二行,\(N\) 个整数 \(m_i\)。

输出格式

输出文件仅一个整数,也就是最小代价。

输入输出样例 #1

输入 #1

4
2 5 3 1

输出 #1

22

思想:

石子合并的话可以看成一个一个区间合并在一起,从小的区间开始合并。
易错点:
刚开始把dp[i][j]理解错了,没有理解到这是前面俩个区间的最小代价,加上本次还得加上sum[i][j](可用前缀和优化)。一开始就是 dp[l][r] = min(dp[l][r],dp[l][j]+dp[j+1][r]),j是间断点。应该是dp[l][r] = min(dp[l][r],dp[l][j]+dp[j+1][r]+sum[i][j])。区间长为1时代价是0,不是m[i]。
代码:

点击查看代码
#include <bits/stdc++.h>
using namespace std;
int n;
int m[310];
int dp[310][310],s[310];    //dp是总代价,sum是该次合并的代价
int main()
{
    cin>>n;
    memset(dp,0x3f,sizeof(dp));
    for(int i = 1;i <= n;i++)    cin>>m[i];
    for(int i = 1;i <= n;i++)    s[i] = s[i-1]+m[i];
    for(int i = 1;i <= n;i++)    dp[i][i] = 0;    //区间为1代价是0,不是m[i]
    for(int len = 2;len <= n;len++)
    {
        for(int l = 1;l+len-1 <= n;l++)
        {
            int r = l+len-1;
            for(int j = l;j < r;j++)    //枚举断点
            {
                dp[l][r] = min(dp[l][r],dp[l][j]+dp[j+1][r]);
            }
            dp[l][r] += s[r] - s[l-1];
        }
    }
    cout<<dp[1][n];
    return 0;
}

posted @ 2026-08-13 14:12  lucky_lh  阅读(14)  评论(0)    收藏  举报