【codevs1257】 打砖块

http://codevs.cn/problem/1257/ (题目链接)

题意

  在等腰三角形上打砖块,总共有m发炮弹,每块砖有一个权值,求打出的最大权值

Solution 

  今天考试题,考场上的2个小时就此荒废了,1班是毛戏了。。看来dp还是有待提高,是时候多做做题了。 

  此题一看就是dp对吧,就算不知道看了标签也知道了吧= =。我们把三角变成一个直角三角,于是我们发现,若要打第i列的第j个砖块,那么必须至少打掉第i+1列的前j-1个砖块以及第i列的前j-1个砖块,而打掉第i+1列前j-1个砖块就必须至少打掉第i+1列前j-2个砖块以及第i+2列前j-2个砖块……这样递归下去,形状大概长这样: 
  这里写图片描述 
  比如说我们要打掉第1列第3行的4,我们就必须先打掉以(2,2)为顶点的三角形和一个矩形,如果有更多的子弹,那么就可以打掉以(2,3)为顶点的三角形中的一些点,前提是第2列从1到3都被打掉。也就是说要打掉第i列第j行的砖块,只与第i+1列的砖块有关。 

  于是我们可以写出dp方程。用a[i][j]表示从第i列的第1行到第j行的权值和,用f[i][j][k]表示打掉第i列第j行的砖块,已经打掉了k个。所以方程:f[i][j][k]=max(f[i][j][k],f[i+1][p][k-j]+a[j][i]),p=[]。 

  但是我们还要考虑一种情况,就拿上面那幅图来说吧,如果我们有4发子弹,那么答案是多少呢?打掉第一行的第1、2、4块和第二行第1块是最优的,答案为16。我们发现这个最优答案它并不是由上面我们所说的那样第i列第j行的答案只与后一列有关,它是间断的。 
  这里写图片描述 
  怎么处理呢?我们可以再加入一行:第0行,第0行用来记录不取当前第i列中的砖块,直接取第i-1列中的砖块所得到的最大值,而这个并不与之前的dp方程矛盾,所以就做出来了。

代码

// codevs1257
#include<algorithm>
#include<iostream>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<cmath>
#define LL long long
#define inf 2147483640
#define Pi acos(-1.0)
#define free(a) freopen(a".in","r",stdin),freopen(a".out","w",stdout);
using namespace std;
inline int getint() {
    int f,x=0;char ch=getchar();
    while (ch<'0' || ch>'9') {if (ch=='-') f=-1;else f=1;ch=getchar();}
    while (ch>='0' && ch<='9') {x=x*10+ch-'0';ch=getchar();}
    return f*x;
}

const int maxn=100;
int n,m;
int a[maxn][maxn],f[maxn][maxn][600],t[maxn];

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]);a[i][j]+=a[i-1][j];}
    for (int i=1;i<=n;i++) t[i]=t[i-1]+i;
    t[0]=1;int ans=0;
    for (int i=n;i>=1;i--)
        for (int j=0;j<=n-i+1;j++) {
            for (int k=t[j];k<=m;k++)
                for (int p=max(0,j-1);p<=n-i+1;p++)
                    f[i][j][k]=max(f[i][j][k],f[i+1][p][k-j]+a[j][i]);
            ans=max(ans,f[i][j][m]);
            }
    printf("%d",ans);
    return 0;
}

  

posted @ 2016-09-27 21:07  MashiroSky  阅读(297)  评论(0编辑  收藏  举报