洛谷题单指南-动态规划3-P1880 [NOI1995] 石子合并

原题链接:https://www.luogu.com.cn/problem/P1880

题意解读:计算n堆石子合并的最小、最大得分,只不过这n堆石子是环形的,也就是首、尾也相邻,是区间DP的升级版-环形DP问题。

解题思路:

如果是常规区间DP的方法:

对于n堆石子,考察区间的长度范围是1 ~ n

先枚举左端点i,范围是1 ~ n

再计算右端点j,结果是 i + len - 1,如果是普通区间问题,i + len - 1必须<=n,但是由于是环形,当左端点i = n时,右端点最大可以到i + len - 1 = n + n - 1 = 2n - 1

因此,可以将石子a[n]重复2倍长度,变成a[2n],在a[2n]上进行区间dp,分别计算合并n堆石子的最小、最大得分即可。

1、状态表示:

设f[i][j]表示将i ~ j的石子合并的最小得分,设g[i][j]表示将i ~ j的石子合并的最大得分

设a[2n]表示两组重复的1~n堆石子,s[2n]是前缀和

2、状态转移

f[i][j] = min(f[i][j], f[i][k] + f[k+1][j] + s[j] - s[i-1])

g[i][j] = max(g[i][j], g[i][k] + g[k+1][j] + s[j] - s[i-1])

3、初始化

f[i][j]初始化为极大值,g[i][j]初始化为极小值

4、结果

最小得分:所有f[i][i+n-1]的最小值

最大得分:所有g[i][i+n-1]的最大值

100分代码:

#include <bits/stdc++.h>
using namespace std;

const int N = 105;
int n;
int a[2 * N], s[2 * N];
int f[2 * N][2 * N], g[2 * N][2 * N];

int main()
{
    cin >> n;
    for(int i = 1; i <= n; i++)
    {
        cin >> a[i];
        a[i + n] = a[i]; //石子重复两组
    }

    for(int i = 1; i <= 2 * n; i++)
    {
        s[i] = s[i - 1] + a[i];
    }

    memset(f, 0x3f, sizeof(f));
    memset(g, -0x3f, sizeof(g));

    for(int len = 1; len <= n; len++)
    {
        for(int i = 1; i + len - 1 <= 2 * n; i++)
        {
            int j = i + len - 1;
            if(len == 1) f[i][j] = g[i][j] = 0;
            else
            {
                for(int k = i; k < j; k++)
                {
                    f[i][j] = min(f[i][j], f[i][k] + f[k+1][j] + s[j] - s[i-1]);
                    g[i][j] = max(g[i][j], g[i][k] + g[k+1][j] + s[j] - s[i-1]);
                }
            }  
        }
    }

    int minans = 0x3f3f3f3f;
    int maxans = -0x3f3f3f3f;
    for(int i = 1; i <= n; i++)
    {
        minans = min(minans, f[i][i+n-1]);
        maxans = max(maxans, g[i][i+n-1]);
    }
    cout << minans << endl << maxans;

    return 0;
}

 

posted @ 2024-05-13 15:07  五月江城  阅读(36)  评论(0编辑  收藏  举报