题目:codevs1257、洛谷P1437
题目大意:有一些砖块呈倒三角形状,每块砖敲掉后有一个分数。除第一行外,敲掉一块砖必须先把上面两块砖敲掉。现在你能敲m块砖,求能得到的最大分数。
解题思路:此题是一道非常恶心的dp。我们先把砖块“左对齐”,然后敲掉砖块(i,j)(i>1)时,就必须先敲掉(i-1,j)和(i-1,j+1)。
设$f[i][j][k]$表示打到第i列第j块砖,一共打了k块砖时所得的分数,则有$f[i][j][k]=max(f[i][j][k],f[i+1][p][k-j](j-1\le p<n-i+1)+\sum\limits_{v=1}^j a[v][i])$。其中求第i列前v块砖之和可以直接预处理出来。因为求第i列时要用到第i+1列的东西,所以枚举i时应该从大到小。答案就在dp的时候顺便求出。
时间复杂度$O(n^3m)$,然而常数很小。
注意行和列千万别搞混了。
还有codevs和洛谷的m的范围是不一样的,codevs里是$1\le m \le 500$,洛谷大概是$1\le m \le 1275$。我的f开到52*52*1300,稳过。
C++ Code:
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int a[52][52],sum[52][52],f[52][52][1300];
int n,m;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
for(int j=1;j<=n-i+1;++j){
scanf("%d",&a[i][j]);
}
}
for(int j=1;j<=n;++j){
for(int i=1;i<=n-j+1;++i)
sum[i][j]=sum[i][j-1]+a[j][i];
}
memset(f,-1,sizeof f);
f[n+1][0][0]=0;
int ans=0;
for(int i=n;i;--i){
for(int j=0;j<=n-i+1;++j)
for(int k=j;k<=m;++k)
for(int p=(j)?j-1:0;p<n-i+1;++p){
if(f[i+1][p][k-j]!=-1){
f[i][j][k]=max(f[i][j][k],f[i+1][p][k-j]+sum[i][j]);
ans=max(ans,f[i][j][k]);
}
}
}
printf("%d\n",ans);
return 0;
}