P1880合并石子

题目链接:

合并石子

题意分析:

这道题是区间Dp的经典题,对于一个区间内的石子,每次合并相邻的两堆石子,求出最大和最小得分。

思路分析:

Dp的状态表示可以从最后一步来思考,假设最后一步是合并已经合并完成的两堆石子,考虑最大值,f[i][j]表示区间[i,j]合并的最大值,枚举最后一步来得到转移方程,即最后合并的两堆是[i,k]和[k+1,r],其中i<=k<r.

Dp方程:f[i][j]表示合并区间[i,j]的最大值;

Dp转移f[i][j]=max(f[i][k]+f[k+1][r]+sum[j]-sum[i-1],f[i][j]) i<=k<r, sum[i]表示前缀和。

坑点:

1.这道题是环形区间dp,一般的套路就是将区间[1,n]变为[1,2n],在[1,2n]中取区间长度为n的区间来代替环形的效果。

2.对于区间Dp,很多时候需要从长度较小区间来递推到长度较大区间,所以常见的枚举方式是从小到大枚举区间长度;

3.注意f[i][i]=0和一些初始值的初始化。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N=310*2;
int f[N][N],g[N][N];
int s[N];
int w[N];
int n;
int main()
{
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>w[i];
        w[i+n]=w[i];
    }
    for(int i=1;i<=n*2;i++)
        s[i]=s[i-1]+w[i];
    memset(f,0x3f,sizeof(f));
    memset(g,-0x3f,sizeof(g));
    for(int i=1;i<=n*2;i++)
        f[i][i]=g[i][i]=0;
    for(int d=2;d<=n;d++){
        for(int l=1;l+d-1<=2*n;l++){
            int r=l+d-1;
            for(int k=l;k<r;k++){
                f[l][r]=min(f[l][r],f[l][k]+f[k+1][r]+s[r]-s[l-1]);
                g[l][r]=max(g[l][r],g[l][k]+g[k+1][r]+s[r]-s[l-1]);
            }
        }
    }
    int res1=1e9,res2=0;
    for(int i=1;i<=n;i++){
        res1=min(res1,f[i][i+n-1]);
        res2=max(res2,g[i][i+n-1]);
    }
    cout<<res1<<"\n";
    cout<<res2<<"\n";
    return 0;
}

posted @ 2020-11-22 21:48  Taoger_Xu  阅读(71)  评论(0)    收藏  举报