【codevs1257】 打砖块

来源:互联网 发布:ip协议的端口号 编辑:程序博客网 时间:2024/05/03 09:31

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方程矛盾,所以就做出来了。

代码:

#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;}
0 0
原创粉丝点击