P1880石子合并

1995年的noi区间dp题,这道题AC耗时达到了数月。

有一道题叫做果子合并,也是求合并的最小花费,但是那个题是可以随便合并两堆,但是这个题只能合并相邻的两堆,并且是一个环。对于环的问题,我们一般可以特判i==1和i==n或者倍增转化成链的问题,长度为两倍的时候即可涵盖所有环的情况。meanwhile我们发现了一个问题,当前要合并的两堆一定是要重合的,所以转化到了区间的问题,然后每一个最优子结构的状态是由其中的两个小小最优子结构而转移来的,所以枚举分割点即可。这样就建立了区间dp的模型。要注意最后还要进行判断,因为起点是1~2n。

1.熟记这个区间dp模型,当成模板题

2.分析每一个状态是怎么来的

3.正确处理环的做法

代码

#include<bits/stdc++.h>
#define maxn 300
using namespace std;
int dpMin[maxn][maxn];
int dpMax[maxn][maxn];
int a[maxn];//石头数目 
int sum[maxn];//前缀和
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++){
        sum[i]=sum[i-1]+a[i];//前缀和
    }
    memset(dpMax,-1,sizeof dpMax);
    memset(dpMin,0x3f,sizeof dpMin);
    for(int i=1;i<=n*2;i++){
        dpMin[i][i]=0;
        dpMax[i][i]=0;
    }
    //区间DP 
    for(int len=2;len<=n;len++){
        for(int i=1;i<=n*2;i++){
            int j=i+len-1;
            if(j>2*n) break;
            for(int k=i;k<j;k++){
                dpMin[i][j]=min(dpMin[i][j],dpMin[i][k]+dpMin[k+1][j]+sum[j]-sum[i-1]);
                dpMax[i][j]=max(dpMax[i][j],dpMax[i][k]+dpMax[k+1][j]+sum[j]-sum[i-1]);
            }    
        }
    }
    int ansMax=dpMax[1][n];
    int ansMin=dpMin[1][n];
    for(int i=1;i<=n;i++){
        ansMax=max(dpMax[i][i+n-1],ansMax);
        ansMin=min(dpMin[i][i+n-1],ansMin);
    } 
    cout<<ansMin;    
    puts(" ");    
    cout<<ansMax;
    return 0;
}

 

posted @ 2019-10-12 18:12  毛炯人  阅读(138)  评论(0编辑  收藏  举报