题目大意
在一个凹槽中放置了$n$层砖块,最上面的一层有$n$块砖,从上到下每层依次减少一块砖。每块砖都有一个分值,敲掉这块砖就能得到相应的分值,如图所示。
如果你想敲掉第$i$层的第$j$块砖的话,若$i=1$,你可以直接敲掉它,若$i>1$,则你必须先敲掉第$i-1$层的第$j$和第$j+1$块砖。
你现在可以敲掉最多$m$块砖。输入$n$,$m$和这$n$层砖块($1 leqslant n leqslant 50$,$1 leqslant m leqslant 500$),求得分最多能有多少。
题解
我们可以想到,对于第$j$列的砖块,只与第$j$列和第$j + 1$列有关。如果我们用$(i,j)$表示第$i$层第$j$个砖块,那么敲掉$(i,j)$,就相当于敲掉$(1...i,j)$,然后再敲掉$(i - 1, j + 1)$。
那我们可以从第$n$列到第$1$列进行dp,设$dp[i][j][k]$表示在第$j$到第$n$列,敲掉砖块第$i$层的第$j$个砖块,一共敲了$k$个砖快。容易得到状态转移方程
$$dp[i][j][k] = sum_{i' = 1}^{i} a[i'][j] + underset{max {0, i - 1 } leqslant i' leqslant n - j}{max} { dp[i'][j + 1][k - i] }$$
注意一下边界即可。
#include <iostream> #define MAX_N (50 + 5) #define MAX_M (500 + 5) using namespace std; int n, m; int a[MAX_N][MAX_N]; int dp[MAX_N][MAX_N][MAX_M]; int mdp[MAX_N][MAX_N][MAX_M]; int ans; int main() { cin >> n >> m; for(int i = 1; i <= n; ++i) { for(int j = 1; j <= n - i + 1; ++j) { cin >> a[i][j]; a[i][j] += a[i - 1][j]; } } for(int j = n; j; --j) { for(int i = n - j + 1; i >= 0; --i) { for(int k = (i + 1) * i / 2; k <= m; ++k) { dp[i][j][k] = mdp[max(i - 1, 0)][j + 1][k - i] + a[i][j]; mdp[i][j][k] = max(mdp[i + 1][j][k], dp[i][j][k]); ans = max(ans, dp[i][j][k]); } } } cout << ans; return 0; }