hihoCoder1636 Pangu and Stones(区间dp,2017北京)

题意:

有n堆石子,第i堆的数量为a(i),
每次只能将连续的石子合并为一堆,代价为这些石子总数量,要求合并的堆数在[L,R]之间。
求将所有石子合并为一堆的最小代价。
如果无解输出0.

数据范围:n<=100

解法:

比普通的石子合并多了一个堆数的限制

-----分割线-----

claris的题解:
普通的石子合并,f[l][r]表示将[l,r]合并为一堆的最小代价,
通过枚举分割点进行转移:
f[l][r]=min{f[l][k]+f[k+1][r]}

这题f[l][r][k]表示[l,r]合并成k堆的最小代价
g[l][r]表示[l,r]合并为一堆的最小代价

f[l][r][k]=min{f[l][x][k-1]+g[x+1][r]},
g[l][r]=min{f[l][r][x]}+sum(l,r),其中L<=x<=R

-----分割线-----

g[l][r]可以用f[l][r][1]代替

code:

#include<bits/stdc++.h>
using namespace std;
const int maxm=105;
int d[maxm][maxm][maxm];
int sum[maxm];
int a[maxm];
int n,L,R;
signed main(){
    while(scanf("%d%d%d",&n,&L,&R)!=EOF){
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<=n;i++)sum[i]=sum[i-1]+a[i];
        //init
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                for(int k=1;k<=n;k++){
                    d[i][j][k]=1e9;
                }
            }
        }
        for(int i=1;i<=n;i++)d[i][i][1]=0;
        //
        for(int len=1;len<=n;len++){//区间长度
            for(int i=1;i<=n;i++){//区间左端点
                int j=i+len-1;
                if(j>n)break;//区间右端点
                for(int k=2;k<=min(R,len);k++){//堆数
                    for(int x=i;x<j;x++){//切点
                        d[i][j][k]=min(d[i][j][k],d[i][x][k-1]+d[x+1][j][1]);
                    }
                    if(k>=L)d[i][j][1]=min(d[i][j][1],d[i][j][k]+sum[j]-sum[i-1]);//g[i][j]
                }
            }
        }
        if(d[1][n][1]==1e9)d[1][n][1]=0;
        printf("%d\n",d[1][n][1]);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_44178736/article/details/107805618