洛谷P1880 [NOI1995] 石子合并(区间dp)

题目

题解

这个题目是个环形,我们可以拆换成链,把这个环拉直,然后变成一个长度为2*n的链,这样就可以覆盖环形时的所有情况了.

然后接下来就是区间dp

我们很难直接的找出先合并哪一些是最优的,然后举个例子

比如说总共有4堆石子   我们要将它们合并 那么合并的最后一步有哪几种方案

第一种 把第一堆和后三堆已合并完的合并起来

第二种 前两堆 和 后两堆 合并起来 ( 都指合并完)

第三种 前三堆 和 第四堆

也就是说对于一个区间[i,j] 我们可以枚举一个中间的点k  ,  找到最优的那一种

因为我们是从区间长度短的来获得长的所以我们需要枚举区间长度

下面结合代码看

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N = 320;
ll n,a[N],dp[N][N],fdp[N][N],pre[N]; // dp和fdp一样,一个维护最小一个最大
void solve(){
    cin >> n;
    for(int i=1;i<=n;i++) {
        cin >> a[i];
        a[i+n]=a[i];   //拆成a[2*n]
    } 
    for(int i=1;i<=2*n;i++)
    pre[i]=pre[i-1]+a[i];  // 维护前缀和 合并的时候要计算区间和
    for(int len=2;len<=n;len++) //枚举长度
        for(int i=1,j=i+len-1;j<=2*n;i++,j++) // 枚举起点
        {
            //i是左端点 ,j 是 右端点 dp[i][j] 就是合并[i,j]的最优 
            dp[i][j]=4e18;// 初始化
            for(int k=i;k<j;++k) // //枚举中间点
            {
                //比较,找到最优
                dp[i][j]=min(dp[i][j],dp[i][k]+dp[k+1][j]+pre[j]-pre[i-1]);
                fdp[i][j]=max(fdp[i][j],fdp[i][k]+fdp[k+1][j]+pre[j]-pre[i-1]);
            }
           
        }
    ll ma=fdp[1][n],mi=dp[1][n];//环形,找到长度为n的区间的最优解
    for(int i=1;i<=n;i++){
        if(ma<fdp[i][n+i-1]) ma=fdp[i][n+i-1];
        if(mi>dp[i][n+i-1])  mi=dp[i][n+i-1];
    }
    cout << mi <<'\n' <<  ma; 
}
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int _ =1;
    //cin >> _;
    while(_--)solve();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值