首页 > 其他 > 详细

[luogu1880] [NOI1995]石子合并

时间:2018-10-24 22:54:02      阅读:147      评论:0      收藏:0      [点我收藏+]

传送门

Solution

看上去的确是一道dp题,还是区间dp的那种

但是你发现这是一个环,非常难受

所以我们想啊:如果这是一条链该多好

于是乎,我们直接把这个环拉直。1 2 3

但是怎么绕回去呢?

再接一遍好了:1 2 3 1 2

这样就可以了!!

那么如何进行区间dp呢?

首先我们定义\(dp[i][len]\)为合并\([i,i+len-1]\)这段区间的最优值

那么我们一定可以找到一个中点\(k\),使得我们先合并\(dp[i][k-i+1]\)\(dp[k+1][i+len-k-1]\),然后我们再通过这两个区间,合并出\(dp[i][len]\)

那么石子合并产生的权值如何计算?其实就是\([i,i+len-1]\)这段区间内的石子数之和,这个我们可以用前缀和维护

综上所述,\(dp[i][len] = max/min(dp[i][len],dp[i][k-i+1]+dp[k+1][i+len-k-1]+w[i,i+len-1])(k \in [i,i+len-1])\)

#include <cstdio>
#include <cstring>
#include <algorithm>
#define MAXN 205
#define INF 2147483647

int dp1[MAXN][MAXN],dp2[MAXN][MAXN];
int Prefix[MAXN];
int a[MAXN];
int N;

int main() {

    scanf("%d",&N);
    std::memset(dp1,0,sizeof(dp1));
    std::memset(dp2,0,sizeof(dp2));
    for(register int i=1;i<(N<<1);++i) {
        for(register int j=2;j<=N;++j) dp2[i][j] = INF;
    }
    Prefix[0] = 0;

    for(register int i=1;i<=N;++i) {
        scanf("%d",&a[i]);
        Prefix[i] = a[i] + Prefix[i-1];
    }

    for(register int i=N+1;i<(N<<1);++i) {
        Prefix[i] = Prefix[i-1] + a[i-N];
    }

    for(register int len=2;len<=N;++len) {
        for(register int i=1;i+len-1<(N<<1);++i) {
            for(register int k=i;k<i+len-1;++k) {
                dp1[i][len] = std::max(dp1[i][len],dp1[i][k-i+1]+dp1[k+1][i+len-k-1]+Prefix[i+len-1]-Prefix[i-1]);
                dp2[i][len] = std::min(dp2[i][len],dp2[i][k-i+1]+dp2[k+1][i+len-k-1]+Prefix[i+len-1]-Prefix[i-1]);
            }
        }
    }

    int ans_max = 0;
    int ans_min = INF;
    for(register int i=1;i<=N;++i) {
        ans_max = std::max(ans_max,dp1[i][N]);
        ans_min = std::min(ans_min,dp2[i][N]);
    }
    printf("%d\n%d\n",ans_min,ans_max);
    return 0;
}

[luogu1880] [NOI1995]石子合并

原文:https://www.cnblogs.com/Neworld2002/p/9846460.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!