zoukankan      html  css  js  c++  java
  • J

    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
    ",i,j,k,dp[i][j][k],i,j,pre[i][j]);
                        }
                    }
                }
            }
            ll ans=dp[1][n][1];
            if(ans<inf64) printf("%lld
    ",ans);
            else printf("0
    ");
        }
        return 0;
    }
    
  • 相关阅读:
    Count and Say
    Roman to Integer LeetCode Java
    白菜刷LeetCode记-121. Best Time to Buy and Sell Stock
    白菜刷LeetCode记-103. Binary Tree Zigzag Level Order Traversal
    白菜刷LeetCode记-102. Binary Tree Level Order Traversal
    白菜刷LeetCode记-350. Intersection of Two Arrays II
    白菜刷LeetCode记-268. Missing Number
    白菜刷LeetCode记-378. Kth Smallest Element in a Sorted Matrix
    白菜刷LeetCode记-328. Odd Even Linked List
    白菜刷LeetCode记-230. Kth Smallest Element in a BST
  • 原文地址:https://www.cnblogs.com/EchoZQN/p/13265283.html
Copyright © 2011-2022 走看看