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

 题目链接:

[NOI1995] 石子合并 - 洛谷https://www.luogu.com.cn/problem/P1880参考了博客:【洛谷P1880】合并石子_sdfzchy的博客-CSDN博客 

思路:

典型区间dp题,以求最小值为例,令dp[ i ][ j ]表示从 i 到 j 区间的石子合并得到的最小得分,当前dp值 = 合并的两堆本来就有的dp值之和 + 当前合并得到的dp值(sum[ i ][ j ])。

dp递推式如下:

细节处理:

1.要模拟环形,需要把n长度的数组扩展到2*n-1 。

2.sum可以用前缀和计算。

代码:

#include <iostream>
#include <queue>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <vector>
using namespace std;
const int inf = 0x3f3f3f3f;
const int maxn = 2e2+5; //实际maxn的两倍(因为要模拟环形)
int n; //石子数量
int sum[maxn]; //重量前缀和
int dp[maxn][maxn]; //区间

int dpMin(){
    memset(dp, inf, sizeof(dp));
    for(int i=1; i<=2*n-1; i++) dp[i][i] = 0;
    for(int len=2; len<=n; len++){
        for(int s=1; s<=2*n-len; s++){
            int e = s+len-1;
            for(int k=s; k<e; k++){
                dp[s][e] = min(dp[s][e], dp[s][k]+dp[k+1][e]);
            }
            dp[s][e] += sum[e] - sum[s-1];
        }
    }
    int ans = inf;
    for(int i=1; i<=n; i++){
        ans = min(ans, dp[i][i+n-1]);
    }
    return ans;
}
int dpMax(){
    memset(dp,0,sizeof(dp));
    for(int len=2; len<=n; len++){
        for(int s=1; s<=2*n-len; s++){
            int e = s+len-1;
            for(int k=s; k<e; k++){
                dp[s][e] = max(dp[s][e], dp[s][k]+dp[k+1][e]);
            }
            dp[s][e] += sum[e] - sum[s-1];
        }
    }
    int ans = 0;
    for(int i=1; i<=n; i++){
        ans = max(ans, dp[i][i+n-1]);
    }
    return ans;
}

int main(){
    cin >> n;
    for(int i=1; i<=n; i++){//输入(注意环形)
        cin >> sum[i];
        sum[i+n] = sum[i];
    }
    for(int i=1; i<=2*n-1; i++){ //求前缀和
        sum[i] += sum[i-1];
    }
    cout << dpMin() << endl;
    cout << dpMax() << endl;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值