J - Pangu and Stones 区间dp

J - Pangu and Stones 区间dp

题目大意:

给你n堆石头,对这n堆石头进行合并,要求每次合并的石头堆数不能小于 \(l\),也不能大于 \(r\) ,每次合并的代价是合并的石头的数量,问将所有的石头合并的最小代价是多少,如果无法合并成一堆则输出0。

题解:

这个和石头合并很像,但是多了一维石头的堆数,所以可以定义 \(dp[i][j][k]\) 表示从 \(i\)\(j\)\(k\) 堆石头的最小代价。最后答案输出 \(dp[1][n][1]\) 即可。

但是中间要保证每次合并数量 \(x\) 是在区间 \([l,r]\) ,合并有两种方式:

  • 第一种就是合并成一堆,这个则需要花费代价
  • 第二种就是 \(k\) 堆由 1 和 \(k-1\) 组合在一起,这个不需要花费代价。为什么是 1 和 \(k-1\),而不是 \(x\)\(k-x\) 这个应该显而易见,因为我只需要找到第一堆的最优位置即可,遍历这个 \(x\) 最后得到的结果其实和找到第一堆的最优位置是一样的。

转移方程:

\(k==1\) \(dp[i][j][k]=min(dp[i][j][k],dp[i][x][1]+dp[x+1][j][y]+sum[j]-sum[i-1])\)

其他 \(dp[i][j][k]=min(dp[i][j][k],dp[i][x][1]+dp[x+1][j][k-1])\)

但是注意当 \(k==1\) 的时候,这个转移必须合法转移,也就是合并的堆数一定要在区间 \([l,r]\),所以需要一个数组来求 $dp[x+1][j][y] $ y必须满足条件 : \(l-1=<y<=r-1\)

所以就重新开一个数组 \(pre[i][j]\) 表示区间\([l,r]\) 合并的堆数是 \([l-1,r-1]\) 的最小代价。

所以最后的转移方程就是:

if(k==1) dp[i][j][k]=min(dp[i][j][k],dp[i][x][1]+pre[x+1][j]+sum[j]-sum[i-1]);
else dp[i][j][k]=min(dp[i][j][k],dp[i][x][1]+dp[x+1][j][k-1]);

代码:

#include <bits/stdc++.h>
#define id first
#define val second
#define inf 0x3f3f3f3f
#define inf64 0x3f3f3f3f3f3f3f3f
using namespace std;
const int maxn=110;
typedef long long ll;
ll dp[maxn][maxn][maxn],a[maxn],sum[maxn],pre[maxn][maxn];

int main(){
    int n,l,r;
    while(~scanf("%d%d%d",&n,&l,&r)){
        sum[0]=0;
        memset(dp,inf64,sizeof(dp));
        memset(pre,inf64,sizeof(pre));
        dp[0][0][0]=0;
        for(int i=1;i<=n;i++) {
            scanf("%lld",&a[i]),sum[i]=sum[i-1]+a[i],dp[i][i][1]=0;
            if(1>=l-1&&1<=r-1) pre[i][i]=0;
        }
        for(int len=2;len<=n;len++){
            for(int i=1;i+len-1<=n;i++){
                int j=i+len-1;
                for(int k=1;k<=r;k++){
                    for(int x=i;x<=j;x++){
                        if(k==1) dp[i][j][k]=min(dp[i][j][k],dp[i][x][1]+pre[x+1][j]+sum[j]-sum[i-1]);
                        else dp[i][j][k]=min(dp[i][j][k],dp[i][x][1]+dp[x+1][j][k-1]);
                        if(k>=l-1&&k<=r-1) pre[i][j]=min(pre[i][j],dp[i][j][k]);
//                        printf("dp[%d][%d][%d]=%lld pre[%d][%d]=%lld\n",i,j,k,dp[i][j][k],i,j,pre[i][j]);
                    }
                }
            }
        }
        ll ans=dp[1][n][1];
        if(ans<inf64) printf("%lld\n",ans);
        else printf("0\n");
    }
    return 0;
}
posted @ 2020-07-08 10:10  EchoZQN  阅读(103)  评论(0编辑  收藏  举报