洛谷P1880 石子合并

区间DP

题目传送门

经典区间DP,和能量项链很像。
先把环断成链,再把链*2,就转化成了一个区间问题。

f[l][r] 表示把 [l,r] 这段石子合并所能达到的最大/小值。枚举断点i( i(l,r) ),把原区间分成 [l,i] [i+1,r] 两段求解。
转移方程: f[l][r]=max(f[l][r],dp(l,i)+dp(i,r)+rj=lw[j])
记忆化搜索即可。

代码:

#include
#include
#include
#define MAXN 200
using namespace std;
int n;
int f1[MAXN+5][MAXN+5],f2[MAXN+5][MAXN+5],w[MAXN+5];
int dp(int l,int r,bool flag){
    if (flag&&f1[l][r]) return f1[l][r];
    if (!flag&&f2[l][r]) return f2[l][r];
    if (l==r) return f1[l][r]=f2[l][r]=0;
    f1[l][r]=0; f2[l][r]=0x7fffffff;
    for (int i=l;i1)+dp(i+1,r,1)+w[r]-w[l-1]);
        f2[l][r]=min(f2[l][r],dp(l,i,0)+dp(i+1,r,0)+w[r]-w[l-1]);
    }
    if (flag) return f1[l][r];
    else return f2[l][r];
}
int main(){
    scanf("%d",&n);
    for (int i=1;i<=n;i++){
        scanf("%d",&w[i]);
        w[n+i]=w[i];
    }
    for (int i=1;i<=n*2;i++)
        w[i]+=w[i-1];
    dp(1,2*n,1); dp(1,2*n,0);
    int ans1=0,ans2=0x7fffffff;
    for (int i=1;i<=n;i++){
        ans1=max(ans1,f1[i][n+i-1]);
        ans2=min(ans2,f2[i][n+i-1]);
    }
    printf("%d\n%d\n",ans2,ans1);
    return 0;
}

你可能感兴趣的:(DP---区间DP,洛谷,蒟蒻zxl的Blog专栏)