Luogu 1437 [HNOI2004]敲砖块 (动态规划)

Description

在一个凹槽中放置了 n 层砖块、最上面的一层有n块砖,从上到下每层依次减少一块砖。每块砖都有一个分值,敲掉这块砖就能得到相应的分值,如下图所示。

14 15 4 3 23

33 33 76 2

2 13 11

22 23

31

如果你想敲掉第 i 层的第j 块砖的话,若i=1,你可以直接敲掉它;若i>1,则你必须先敲掉第i-1 层的第j 和第j+1 块砖。你现在可以敲掉最多 m 块砖,求得分最多能有多少。

Input

输入文件的第一行为两个正整数 n 和m;接下来n 行,描述这n层砖块上的分值a[i][j],满足0≤a[i][j]≤100。对于 100%的数据,满足1≤n≤50,1≤m≤n*(n+1)/2;

Output

输出文件仅一行为一个正整数,表示被敲掉砖块的最大价值总和。

Sample Input

4 5

2 2 3 4

8 2 7

2 3

49

Sample Output

19

Http

Luogu:https://www.luogu.org/problem/show?pid=1437

Source

动态规划

解决思路

这是一道非常难想到的动态规划问题。

首先我们把矩阵左对齐,但发现如果我们直接在这个上面对行进行转移,我们发现是不满足转移的有序性的,因为第i行第j列是否可以敲掉取决于上面一个倒三角是否被敲掉,比如说这个图

 1  2  3  4  5
6 7 8 9
10 11 12
13 14
15

如果我们要敲掉7,则1,2都要敲掉。如果我们要敲掉12,则1,2,3,7,8都要敲掉。这给转移带来了麻烦。

如何解决呢?我们发现如果第i行第j列被敲掉了,那么要求对于\(\forall k \in [1,i-1]\),[k][j]一定被打掉了。

于是我们就想到话说这怎么想到的?把矩阵翻折过来。上面的矩阵就变成了这个样子

1
2 6
3 7 10
4 8 11 13
5 9 12 14 15

简单点来说,就是把矩阵沿主对角线翻折,再向下对齐

这个翻转用程序表示就是:

for (int j=1;j<=n;j++)
for(int i=j;i<=n;i++)
Mat_new[i][j]=read();//read就是按照原矩阵的顺序从上至下从左至右读入

那么我们就可以知道,如果要选择[i][j],那么[i][1~(j-1)]是一定要选的,并且我们还发现,原来的选一个数需要选择的上三角变成了更好处理的下三角。举个例子,比如说12

在原来的图中

 1   2   [3] [4] [5]
6 7 [8] [9]
10 11 [12]
13 14
15

把图翻折后

 1
2 6
[3] 7 10
[4] [8] 11 13
[5] [9] [12] 14 15

所以,我们设F[i][j][k]表示在新图中的第i行取前j个总共取了k个的最大的,那么我们只要枚举上一行是由那个转移过来的。

可以注意到,因为我们当前是在第j列,那么我们在上一行的枚举就至少得从j-1列开始。比如上面这个12的例子,那么就至少得从数字8(第2列)开始枚举,枚举到数字13(第4列)

所以我们就可以的得到动态转移方程(Arr就是我们翻转后的矩阵)

\[F[i][j][k]=max(F[i][j][k],F[i-1][p][k-j]+\sum_{l=1}^{l<=j}Arr[i][j])\{p \in [1,j-1]\}
\]

我们发现这个算法还有改进的余地,就是利用前缀和来优化\(\sum_{l=1}^{l<=j}Arr[i][j]\)的求解。

令\(Sum[i][j]=\sum_{k=1}^{k<=i}Arr[i][k]\),那么有

\[Sum[i][j]=Sum[i][j-1]+Arr[i][j]
\]

所以最终的转移方程就是

\[F[i][j][k]=max(F[i][j][k],F[i-1][p][k-j]+Sum[i][j])
\]

代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std; #define ll long long
#define mem(Arr,x) memset(Arr,x,sizeof(Arr)) const int maxN=60;
const int maxM=4000;
const int inf=2147483647; int n,m;
int Arr[maxN][maxN];
int Sum[maxN][maxN];
int F[maxN][maxN][maxM]; int read();
void outp(); int main()
{
n=read();
m=read();
for (int j=1;j<=n;j++)//输入,同时转置矩阵
for(int i=j;i<=n;i++)
Arr[i][j]=read();
for (int i=1;i<=n;i++)//计算前缀和
for (int j=1;j<=i;j++)
Sum[i][j]=Sum[i][j-1]+Arr[i][j];
mem(F,-1);//置为-1,标记为不行
for (int i=1;i<=n;i++)//动态转移初始值
{
F[i][0][0]=0;
F[i][1][1]=Arr[1][i];
}
int Ans=0;
for (int i=1;i<=n;i++)
for (int j=0;j<=i;j++)
for (int k=0;k<=m;k++)
{
if (j<=k)//只用j<k的时候才能推
for (int p=max(j-1,0);p<=i-1;p++)//注意这里的取max,因为j为0的时候j-1是负数
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);
fclose(stdin);
fclose(stdout);
return 0;
} int read()
{
int x=0;
int k=1;
char ch=getchar();
while (((ch>'9')||(ch<'0'))&&(ch!='-'))
ch=getchar();
if (ch=='-')
{
k=-1;
ch=getchar();
}
while ((ch>='0')&&(ch<='9'))
{
x=x*10+ch-48;
ch=getchar();
}
return x*k;
}

Luogu 1437 [HNOI2004]敲砖块 (动态规划)的更多相关文章

  1. luogu P1437 [HNOI2004]敲砖块

    三角形向右对齐后 你想打掉一个砖块,那么你必须打掉右上方的三角形,前缀和维护 若是第i列若是k个,那么它右边的那一列至少选了k-1个 f[i][j][k] 表示从后向前选到第 i 列第j个一共打了k次 ...

  2. 洛谷 P1437 [HNOI2004]敲砖块 解题报告

    P1437 [HNOI2004]敲砖块 题目描述 在一个凹槽中放置了 n 层砖块.最上面的一层有n 块砖,从上到下每层依次减少一块砖.每块砖 都有一个分值,敲掉这块砖就能得到相应的分值,如下所示. 1 ...

  3. 【题解】HNOI2004敲砖块

    题目传送门:洛谷1437 决定要养成随手记录做过的题目的好习惯呀- 这道题目乍看起来和数字三角形有一点像,但是仔细分析就会发现,因为选定一个数所需要的条件和另一个数所需要的条件会有重复的部分,所以状态 ...

  4. [HNOI2004]敲砖块

    题目描述 在一个凹槽中放置了 n 层砖块.最上面的一层有n 块砖,从上到下每层依次减少一块砖.每块砖 都有一个分值,敲掉这块砖就能得到相应的分值,如下图所示. 14 15 4 3 23 33 33 7 ...

  5. P1437 [HNOI2004]敲砖块

    题目描述 在一个凹槽中放置了 n 层砖块.最上面的一层有n 块砖,从上到下每层依次减少一块砖.每块砖 都有一个分值,敲掉这块砖就能得到相应的分值,如下图所示. 14 15 4 3 23 33 33 7 ...

  6. 洛谷P1437 [HNOI2004]敲砖块(dp)

    题目背景 无 题目描述 在一个凹槽中放置了 n 层砖块.最上面的一层有n 块砖,从上到下每层依次减少一块砖.每块砖 都有一个分值,敲掉这块砖就能得到相应的分值,如下图所示. 14 15 4 3 23 ...

  7. yzoj P2343 & 洛谷 P1437 [HNOI2004]敲砖块

    题意 在一个凹槽中放置了N层砖块,最上面的一层油N块砖,从上到下每层一次减少一块砖.每块砖都有一个分值,敲掉这块砖就能得到相应的分值,如图所示. 如果你想敲掉第i层的第j块砖的话,若i=1,你可以直接 ...

  8. [洛谷1437&Codevs1257]敲砖块<恶心的dp>

    题目链接:https://www.luogu.org/problem/show?pid=1437#sub http://codevs.cn/problem/1257/ 不得不说,这个题非常的恶心,在初 ...

  9. luogu P1437 [HNOI2004]尻♂砖块

    传送门 想明白了其实不难 强行瞎扯 这题的限制比较烦,导致了一行行转移几乎不能做(吧) 那么一列列转移呢? 设\(f_{i,j,k}\)表示前\(i\)列,取\(j\)个,其中第\(i\)列取从上往下 ...

随机推荐

  1. gitblit 配置图文详解

    Windows平台下Git服务器搭建 前提是确保存在JDK环境. 第一步:下载Gitblit.下载地址:http://www.gitblit.com/ 第二步:解压缩下载的压缩包即可,无需安装. 第三 ...

  2. 12.18 Daily Scrum

    最近大家确实都很忙,所以所有功能的实现要等到下周.   Today's Task Tomorrow's Task 丁辛 实现和菜谱相关的餐厅列表. 实现和菜谱相关的餐厅列表.             ...

  3. Week2 代码复查

    代码复查 http://blog.fogcreek.com/increase-defect-detection-with-our-code-review-checklist-example/ 这篇博客 ...

  4. 冲刺Two之站立会议9

    今天我们团队主要针对软件的功能进行了改进.因为它目前可以实现视频通话,语音聊天,文件传输和文字聊天的通信功能,我们想要在它的基础上实现临时局域群聊和群聊视频的功能,目前还没有完全实现.

  5. 第二个spring,第五天

    陈志棚:成绩的统筹 李天麟:界面音乐 徐侃:代码算法 完成进度百分之70...会继续努力的!

  6. Mac+Docker环境下xdebug的配置

    由于容器化的需要,前几天我本地也换成了docker环境.就研究了一下docker环境下phpstorm和xdebug的配置. http://www.mmfei.com/?p=453 这个博客给出了一个 ...

  7. shell脚本--数值比较

    用于数值比较的无非大于.小于.等于.大于等于.小于等于这几个. 比较格式: [ 数值1 比较符 数值2 ]   注意左边的括号与数值1之间有一个空格,同样,数值2和右边的括号之间也有空格. 数值比较运 ...

  8. Golang 入门~~基础知识

    变量声明 //通用形式,指定变量名,变量类型,变量值 var name int = 99 fmt.Println(name) //指定变量名,以及变量类型,未指定值的时候默认是类型零值 var age ...

  9. Trouble shooting(问题解决):centos 7 gnome show someting has gone wrong.

    centos 7 升级 内核 3.10,startx启动不了了.进界面也是oh,no!someting has gone wrong . 参见帖子:http://bbs.csdn.net/topics ...

  10. [安全]appscan 使用代理抓取其他客户端的请求

    自己安全测试技能很低, 上级给的安全测试的任务给了自动化组的同事来做, 自己之前使用appscan的时候 只知道使用appscan的内置浏览器测试抓取请求 今天与自动化美女同事沟通发现有一个代理的功能 ...