环形石子合并

题目 环形石子合并

image-e72f3a94

思路分析

在之前那题的基础上加上了个环形 以及最大花费

环形相邻 情况已经碰到很多次了

把链延长两倍,变成 2n,其中 i和 i+n是相同的两个堆,然后直接套 区间DP 模板

状态表示—集合\(f_{en,l,r}\):当前合并的石子堆的大小为 len,且石子堆的左端点是 l,右端点是 r的方案

状态表示—属性\(f_{len,l,r}\):方案的费用最大/最小(本题两者都要求)

状态计算—\(f_{len,l,r}\):

image-e40a81d6

初始状态: \(f_{1,i,i}(1≤i≤n)\)

目标状态:\(f_{n,1,n}\)

len这一维的空间可以省去因为 r−l+1=len,也就保证了在已知 l和 r的情况下,不会出现状态定义重复的情况

这俩题都是基于 相邻两堆石子俩俩合并的 所以用区间dp 如果是任意两堆或者多堆合并 就用贪心

代码实现

#include<bits/stdc++.h>

using namespace std;

const int N=210,M=N*2,INF=0x3f3f3f3f;

int a[M],s[M];

int f[M][M],g[M][M];

int 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<=n*2;i++)

        s[i]=s[i-1]+a[i];

    memset(f,0x3f,sizeof f);//求最小 初始化inf

    memset(g,-0x3f,sizeof g);//求最大 初始化-inf

    for(int len=1;len<=n;len++){ // 枚举区间长度

        for(int i=1;i+len-1<=n*2;i++){// 枚举起点

            int j=i+len-1;//计算终点

            if(len==1){//最小区间特判

                f[i][j]=g[i][j]=0;

                continue;

            }

            for(int k=i;k<=j-1;k++){// 枚举分割点,构造状态转移方程

                //k只能在一边 类似于二分的划法 一般把k放左边 k+1为右区间起点

                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]);

            }

        }

    }

    //目标状态中找出方案 因为是环形 所以不能直接1-n 而是在所以1-n长度中找max/min

    int minv = INF, maxv = -INF;

    for (int l = 1; l <= n; ++ l){

        minv = min(minv, f[l][l + n - 1]);

        maxv = max(maxv, g[l][l + n - 1]);

    }

    printf("%d\n%d\n", minv, maxv);

    return 0;

}

同类题型

视频讲解


⬅️ 棋盘分割 🏠 00-刷题理模型 ➡️ 石子合并