[codevs1048]石子归并

题目描述 Description
有n堆石子排成一列,每堆石子有一个重量w[i], 每次合并可以合并相邻的两堆石子,一次合并的代价为两堆石子的重量和w[i]+w[i+1]。问安排怎样的合并顺序,能够使得总合并代价达到最小。

输入描述 Input Description
第一行一个整数n(n<=100)

第二行n个整数w1,w2…wn (wi <= 100)

输出描述 Output Description
一个整数表示最小合并代价

样例输入 Sample Input
4

4 1 1 4

样例输出 Sample Output
18
    
  
发现每合并两堆已合并石子的代价,为每已合并堆中所有石子重量之和;
如先合并1—2堆、3—4堆石子
再将两个新堆合并时,代价为w[1]+w[2]+w[3]+w[4]
而1—2堆石子合并代价为w[1]+w[2]
3—4堆石子合并代价为w[3]+w[4],
故总代价为w[1]+w[2]+w[3]+w[4]+w[1]+w[2]+w[3]+w[4] = 20
显然不是最优解
  

发现在合并第i—j堆石子时,有合并i—k、k+1—j两堆石子共k(k = j - i)种选择
由此可知我们在求dp[i][j]//dp[i][j]为合并第i到第j堆石子的最小代价
时,必须已知其中任两堆石子i—k、k+1—j的最优合并代价
股采用顺推j,逆推i
枚举k为断点
预处理cost[i][j]为一次合并i—j堆石子(无论先前合并顺序)的代价
递推式:dp[i][j] = min(dp[i][j],dp[i][k] + dp[k + 1][j] + cost[i][j]);

#include<iostream>
#include<cstring>
using namespace std;
const int MAXN = 2000 + 5;
int w[MAXN],dp[MAXN][MAXN],cost[MAXN][MAXN];
int n;
int main()
{
    cin >> n;
    for(int i = 1;i <= n;i ++)
    {
        cin >> w[i];
    }
    memset(dp,0x3f,sizeof(dp));
    for(int i = 1;i <= n;i ++)
    for(int j = i;j <= n;j ++)
    {
        if(i == j)dp[i][j] = 0;
        cost[i][j] = cost[i][j - 1] + w[j];
    }

    for(int j = 2;j <= n;j ++)
    for(int i = j - 1;i >= 1;i --)
    for(int k = i;k < j;k ++)
    {
        dp[i][j] = min(dp[i][j],dp[i][k] + dp[k + 1][j] + cost[i][j]);
    }
    cout << dp[1][n];
    return 0;
}

结:感觉dp型题目的问题有两个
一个是递推式,一个是递推过程
怎样保证在已知需要前提的条件下推出结果
也是这题比较难的部分吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值