ICPC2017北京J题(Pangu and Stones)

链接:https://vjudge.net/problem/HihoCoder-1636
思路:区间dp,可以说是石子合并的加强版,只是因为由相邻合并改为了一个范围合并,所以我们要在原来dp上多加一维,dp[l][r][k]表示从l到r区间被分成k堆所需要的最小值,k=1时表示整个区间合并完成,状态表示如下:
k>2 dp[l][r][k] = min(dp[l][r][k],dp[l][d][1]+dp[d+1][r][k-1])
k=1 dp[l][r][1] = min(dp[l][r][1],dp[l][r][k]+sum(r)-sum(l-1))
巧妙的地方就是把答案放到k=1的地方去,这样我们只用在合并的时候加上前缀和,也算是一个套路吧,分解区间的时候不加前缀和,因为之前的都已经算好了,合并的时候更新当前状态的最优解,加上前缀和。
代码:

#include
using namespace std;
typedef long long ll;
int n,L,R;
const int maxn = 500;
const ll INF = 1e18;
ll dp[maxn][maxn][maxn];
ll sum[maxn];
int a[maxn];

int main(){
    while(~scanf("%d%d%d",&n,&L,&R)){
        sum[0] = 0;
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
            sum[i] = sum[i-1] + a[i];
        }
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                for(int k=1;k<=n;k++){
                    dp[i][j][k] = INF;
                }
            }
        }
        for(int i=0;i<=n;i++)dp[i][i][1] = 0;
        for(int len=2;len<=n;len++){
            for(int l=1;l+len-1<=n;l++){
                int r = l+len-1;
                for(int p=2;p<=len;p++){//枚举大于1的情况
                    for(int k=l;k=L&&k<=R)||(k==0&&len>=L&&len<=R))
                    dp[l][r][1] = min(dp[l][r][1],dp[l][r][k]+sum[r]-sum[l-1]);
                }
            }
        }
        printf("%lld\n",dp[1][n][1]>=INF?0:dp[1][n][1]);
    }
    return 0;
}

你可能感兴趣的:(ICPC2017北京J题(Pangu and Stones))