打砖块(brike)

链接:https://ac.nowcoder.com/acm/problem/210249
来源:牛客网

题目

在这里插入图片描述

在一个凹槽中放置了\ n n 层砖块,最上面的一层有\ n n 块砖,第二层有\ n-1 n−1块,……最下面一层仅有一块砖。第 \ i i 层的砖块从左至右编号为1,2,\dots i1,2,…i,第i层的第j块砖有一个价值a[i,j](a[i,j]\leq 50)a[i,j](a[i,j]≤50)。下面是一个有5层砖块的例子:

如果你要敲掉第 \ i i层的第 \ j j 块砖的话,若 \ i=1 i=1,你可以直接敲掉它,若\ i>1 i>1,则你必须先敲掉第 \ i-1 i−1 层的第j和第\ j+1 j+1 块砖。
你的任务是从一个有n(n\leq 50)n(n≤50)层的砖块堆中,敲掉 (m\leq 500)(m≤500)块砖,使得被敲掉的这些砖块的价值总和最大。


解析

要选 a [ i ] [ j ] a[i][j] a[i][j]那么至少要选 a [ i − 1 ] [ j + 1 ] a[i-1][j+1] a[i1][j+1]
所以对于每个 d p [ i ] [ j ] [ k ] dp[i][j][k] dp[i][j][k]打完了第i行j列的那块砖,一共打了k块砖

d p [ i ] [ j ] [ k ] = m a x ( d p [ l ] [ j + 1 ] [ k − i ] + p r e [ i ] [ j ] , d p [ i ] [ j ] [ k ] ) ; ( i − 1 < = l < = n − j ) dp[i][j][k] = max(dp[l][j+1][k-i] + pre[i][j],dp[i][j][k]); (i-1 <= l <= n - j) dp[i][j][k]=max(dp[l][j+1][ki]+pre[i][j],dp[i][j][k]);i1<=l<=nj)
其中
p r e [ i ] [ j ] pre[i][j] pre[i][j]表示第j列第1行到第i行的砖块分数和

注意状态转移过程中
0 < = i < = n − j + 1 0 <= i <= n - j + 1 0<=i<=nj+1

为啥i = 0 也要更新?因为存在最优解,第j列一块砖块都不打,但是j + x列,和j - y列打了的情况,所以要求dp[0][j][k]把的值,把值传递到j - y列

洛谷中这道题砖块数范围更大,dp数组开大点就行了

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 55;
ll dp[N][N][502];

ll a[N][N];
ll pre[N][N];//前缀和

int main(){
    
    memset(dp, 0x8f, sizeof dp);//每个字节都是0x8f,所以dp[i][j][k]一定都是负数
    int n,m;    cin >> n >> m;
    for(int i = 1; i <= n; ++i)
    for(int j = 1; j <= n + 1 - i; ++j)
        cin >> a[i][j];
    for(int i = 1; i <= n; ++i)
        for(int j = 1; j <= n + 1 - i; ++j)
            pre[i][j] = pre[i-1][j] + a[i][j];
  
    
    dp[0][n + 1][0] = 0;
    
    for(int j = n; j; --j)
    for(int i = 0; i <= n - j + 1; ++i)// 为啥i = 0 也要更新?要考虑 第j列一个都不选的情况,需要通过dp[0][j][l]把值传递过去
    for(int k = i*(i + 1)/2; k <= m; ++k){
          ll t = -1e9;
        for(int l = max(i - 1,0); l <= n + 1 - (j + 1); ++l)
            t = max(t,dp[l][j + 1][k-i]);
          
           
        dp[i][j][k] = max(t + pre[i][j],dp[i][j][k]);
              
    }
  
    
    ll ans = 0;
    for(int i = 1; i <= n; ++i)
    for(int j = 1; j <= n - i + 1 ; ++j)
        ans = max(ans,dp[i][j][m]);
    cout << ans ;
        
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值