区间DP-2017 ACM-ICPC北京赛区 - J - Pangu and Stones

2017 ACM-ICPC北京赛区 - J - Pangu and Stones

题解:

一道让我很兴奋的题。作为一个区间DP刚入门2天的萌新可以推对这道题并且过题。

言归正传:

1,集合:l,r区间合并成k堆石头的方案的答案。
2,属性:最小值。
3,状态:l,r,k(堆)(有时候last点可能也会成为一种状态)
4,last点:我们枚举每个点作为区间划分的时候答案都是可能不同的,所以last点就是区间中任意一点。
那么根据last点和状态我们开始写转移方程:
P点是划分点。

f[l][r][k]=min(f[l][r][k],f[l][p][k-1],f[p+1][r][1])

这里为什么是k-1和k呢?我们仔细想一下,我们想想我们k-1堆的最小不可分子集是不是一堆一堆合并上去的?对吧!所以其实我们k堆通过k-1堆和另外一堆的合并来的,所以相通这个之后我们成功合并成了K堆。
那么如果我们k满足在题目中[l,r]这个区间内的话,是不是我们可以直接把这k堆合并成一堆,并且我们这k堆合并的代价是一定的!他就是区间权值和,所以我们开头需要预处理一下前缀和。这样我们就完成了k堆合并成一堆的任务了!然后直接找答案就可以了。

合成一堆的方程f[l][r][1]=min(…,f[l][r][k]+sum[r]-sum[l-1])

#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e2+7;
const int inf=1e9+7;
int dp[N][N][N];
int a[N],sum[N];
int main(){
    int n,l,r;
    while(scanf("%d%d%d",&n,&l,&r)!=EOF){
        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 len=1;len<=n;len++){
            for(int i=1;i+len-1<=n;i++){
                int j=i+len-1;
                dp[i][j][len]=0;
                for(int k=2;k<=len;k++){
                    for(int p=i;p<=j;p++)
                        dp[i][j][k]=min(dp[i][j][k],dp[i][p][1]+dp[p+1][j][k-1]);
                    if(k>=l&&k<=r)
                        dp[i][j][1]=min(dp[i][j][1],dp[i][j][k]+sum[j]-sum[i-1]);
                }
            }
        }
        if(dp[1][n][1]>=inf) printf("0\n");
        else printf("%d\n",dp[1][n][1]);
    }
}
发布了92 篇原创文章 · 获赞 6 · 访问量 1178

猜你喜欢

转载自blog.csdn.net/weixin_42979819/article/details/103899960