动态规划——洛谷1880石子合并

题目:

题目描述

在一个园形操场的四周摆放N堆石子,现要将石子有次序地合并成一堆.规定每次只能选相邻的2堆合并成新的一堆,并将新的一堆的石子数,记为该次合并的得分。

试设计出1个算法,计算出将N堆石子合并成1堆的最小得分和最大得分.

输入输出格式
输入格式:

数据的第1行试正整数N,1≤N≤100,表示有N堆石子.第2行有N个数,分别表示每堆石子的个数.

输出格式:

输出共2行,第1行为最小得分,第2行为最大得分.

输入输出样例
输入样例#1:

4
4 5 9 4

输出样例#1:

43
54

所属专栏:戳我访问
测评网址:戳我访问
这题与《信息学奥赛一本通》里的例题名字相同,但内容不同。
这题采用动态规划解决。
先来看看这题的简化版


一列石堆,每一堆由一些石子堆成。每一次合并相邻的两堆,代价为两堆石子的个数。最终要将所有的石子合并为一堆,总的代价就是每一次代价的和。

   不同的合并方式代价是不同的,所以希望能够得到最小代价。
输入说明:
   第一行一个数N
   接下来N个数,表示一开始每堆石子的个数
输出说明:
   一个数cost,表示最少的代价
输入样例:
   8
   5 3 4 1 3 2 3 4
输出:
   74
   
数据范围: n<=3000


这题就和《信息学奥赛一本通》里面的“合并石子”一模一样了。
想想这题怎么做,直接分析:
这里分析的方法采用之前讲到的分析方法,如果你不知道这个分析方法,请访问:戳我访问
状态表达:f[i][j]表示在i到j的区间内,把这些石子都合成一堆,最小的代价。
状态转移:f[l][r] = std::min(f[l][r],f[l][k]+f[k+1][r]+num[r]-num[l-1]);
q其中l和r分别是i和j,num为前缀和。
下面的就不写了,不是很重要。


那么跳回到这个问题,这这个问题里面的石子是以一个全的形式排列的,那么,也就是说,这对石子的头和尾是相邻的,遇到这种问题,最好的办法就是把石子的长度变为2,把之前的的石子的数量拷贝一份放到尾部,我说的坑能有点不清楚,举个栗子:
1 2 3 4经过操作了以后就变成了1 2 3 4 1 2 3 4
代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>

int num[1001],f[1001][1001],sum[1001];
int main()
{
    int n;
    std::cin>>n;
    for(int i = 1;i<=n;i++)
    {
        std::cin>>num[i];
        num[i+n] = num[i];
    }
    for(int i = 1;i<n*2;i++)num[i] += num[i-1];
    for(int i = 2;i<=n;i++)
        for(int l = 1;l<n*2-i+1;l++)
        {
            int r = l+i-1;
            f[l][r] = 0x3f3f3f3f;
            for(int k = l;k<r;k++)
                f[l][r] = std::min(f[l][r],f[l][k]+f[k+1][r]+num[r]-num[l-1]);
        }
    int ans = 0x3f3f3f3f;
    for(int i = 1;i<=n;i++)ans = std::min(f[i][i+n-1],ans);
    std::cout<<ans<<std::endl;
    for(int i = 2;i<=n;i++)
        for(int l = 1;l<n*2-i+1;l++)
        {
            int r = l+i-1;
            f[l][r] = 0;
            for(int k = l;k<r;k++)
                f[l][r] = std::max(f[l][r],f[l][k]+f[k+1][r]+num[r]-num[l-1]);
        }
    ans = -0x3f3f3f3f;
    for(int i = 1;i<=n;i++)ans = std::max(f[i][i+n-1],ans);
    std::cout<<ans;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值