敲砖块(codevs 1257)题解

【问题描述】

在一个凹槽中放置了 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 块砖,求得分最多能有多少。

【样例输入】

4 5
   2 2 3 4
   8 2 7
   2 3
   49

【样例输出】

19

【解题思路】

本题为湖南省选2004day2的题,初看题目感觉并不好做,然而看了样例之后,我们可以发现一些很神奇的事情……样例是把倒三角变成一个倒直角三角,于是,我们可以清晰地发现,若要打第i列的第j个砖块,那么必须至少打掉第i+1列的前j-1个砖块以及第i列的前j-1个砖块,因为第i列的前j-1个砖块我们可以循环得出,因此设现在需要打第i列的第j个砖块,总共打了k个砖块,f[i,j,k]就表示这种状态下的最大值。因此我们可以发现,f[i,j,k]只与f[i+1,l,k-j]有关,l为>=j-1的数,用最大的f[i+1,l,k-j]加上这一列上从第1个到第j个得分即可,那么DP方程就出来了,然后再看看题目数据范围,可以做DP,时间复杂度为O(n^3*m)需要注意几点:由于i与i+1有关,因此我们把i倒过来循环会好一些,其次j要从0开始循环,例如样例,当你要打第一列第二个时,你可以选择打第一列第一个,第二列第一个,和第四列第一个,这样第三列就是0了。26行代码,应该是省选题目中代码最短的了……

【代码实现】

 1 var a:array[0..55,0..55] of longint;
 2     i,j,n,m,k,l,max:longint;
 3     f:array[-1..55,-1..55,-1..1300] of longint;
 4 begin
 5  readln(n,m);
 6  for i:=1 to n do
 7   for j:=1 to n-i+1 do
 8    read(a[i,j]);
 9  for i:=n downto 1 do
10   for j:=0 to n-i+1 do
11    for k:=2*j-1 to m do
12     begin
13      max:=0;
14      for l:=j-1 to n-i+1 do
15       if f[i+1,l,k-j]>max then
16        max:=f[i+1,l,k-j];
17      f[i,j,k]:=max;
18      for l:=1 to j do
19       inc(f[i,j,k],a[l,i]);
20     end;
21  for i:=1 to n do
22   for j:=0 to n-i+1 do
23    if f[i,j,m]>max then
24     max:=f[i,j,m];
25  writeln(max);
26 end.  
时间: 2024-12-26 04:14:39

敲砖块(codevs 1257)题解的相关文章

Codevs 1257 打砖块

1257 打砖块 http://codevs.cn/problem/1257/ 题目描述 Description 在一个凹槽中放置了n层砖块,最上面的一层有n块砖,第二层有n-1块,……最下面一层仅有一块砖.第i层的砖块从左至右编号为1,2,……i,第i层的第j块砖有一个价值a[i,j](a[i,j]<=50).下面是一个有5层砖块的例子.如果你要敲掉第i层的第j块砖的话,若i=1,你可以直接敲掉它,若i>1,则你必须先敲掉第i-1层的第j和第j+1块砖. 你的任务是从一个有n(n<=5

打砖块(codevs 1257)

题目描述 Description 在一个凹槽中放置了n层砖块,最上面的一层有n块砖,第二层有n-1块,--最下面一层仅有一块砖.第i层的砖块从左至右编号为1,2,--i,第i层的第j块砖有一个价值a[i,j](a[i,j]<=50).下面是一个有5层砖块的例子.如果你要敲掉第i层的第j块砖的话,若i=1,你可以直接敲掉它,若i>1,则你必须先敲掉第i-1层的第j和第j+1块砖. 你的任务是从一个有n(n<=50)层的砖块堆中,敲掉(m<=500)块砖,使得被敲掉的这些砖块的价值总和

【题解】HNOI2004敲砖块

题目传送门:洛谷1437 决定要养成随手记录做过的题目的好习惯呀- 这道题目乍看起来和数字三角形有一点像,但是仔细分析就会发现,因为选定一个数所需要的条件和另一个数所需要的条件会有重复的部分,所以状态不好转移,也会产生后效性. 但是,通过将所有的砖块左移,我们可以发现(i, j)砖块所需要的条件就是(i-1, j) (i-1, j+1)这两块砖均被敲掉. 所以dp方程顺理成章:i,j,k分别表示从第i列j行开始算起,取k个数所能获得的最大价值和. dp[i][j][s] = max(dp[i+1

@@P1437 [HNOI2004]敲砖块

题目描述 在一个凹槽中放置了 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 块砖,求得分最多能有多少. 输入输出格式 输入格式: 输入文件的第一行为两个正整数 n 和m:接下来

luogu 1437 敲砖块(DP)

这道题的DP的状态设计的很有想法啊. 假如我们一行一行来选择的话,状态将会极其复杂. 如果一列一列来看的话,比如你想选aij,那么第i列的前j个都要选,并且第i+1列的前j-1个都要选. 于是状态就很好设计了,定义dp[n][i][j]表示还剩下n个要选的砖块,当前选择第i列的前j个所能达到的最大分值. 那么dp[n][i][j]=max(dp[n-j][i+1][k]+sum[i][j])(j-1<=k<=n-i). 记忆化搜索一下就OK了. # include <cstdio>

luogu P1437 [HNOI2004]敲砖块

三角形向右对齐后 你想打掉一个砖块,那么你必须打掉右上方的三角形,前缀和维护 若是第i列若是k个,那么它右边的那一列至少选了k-1个 f[i][j][k] 表示从后向前选到第 i 列第j个一共打了k次的分数 // luogu-judger-enable-o2 #include<cstdio> #include<cstring> #include<algorithm> using std::max; const int maxn = 57; #define INF 0x7

5.13 校内模拟赛

... 果然是dalao们做难题做多了后 简单题水不起来了吗.. 5.13解题报告 300分 T1写了差不多30~40分钟 T2写了不到5min (当时怀疑有坑..) T3推了大概1个多小时的式子, 然后又加上调试差不多一个半小时 时间分配就这样..感觉T2出的太过简单了..直接是个模板 T1 并查集 + 乱搞 T2 快速幂模板 T3 Dp T1 : codevs 2796 最小完全图 二次联通门 : codevs 2796 最小完全图 /* codevs 2796 最小完全图 并查集 + 乱搞

HDU 1257 最少拦截系统(最长递减子序列的条数)

题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1257 题解: 1 #include<iostream> 2 #include<cstdio> 3 using namespace std; 4 5 const int maxn = 1000 + 10; 6 7 int dp[maxn],arr[maxn]; 8 int n; 9 10 int main() { 11 while (scanf("%d", &

51nod 1257 背包问题 V3(这不是背包问题是二分)

题目链接:https://www.51nod.com/onlineJudge/questionCode.html#!problemId=1257 题解:不能按照单位价值贪心,不然连样例都过不了 要求的r=sum(x[i]*p[i])/sum(x[i]*w[i])不妨设一个辅助函数 z(l)=sum(x[i]*p[i])-l*sum(x[i]*w[i]), 如果z(l) > 0 即sum(x[i]*p[i])-l*sum(x[i]*w[i])>0-->sum(x[i]*p[i])/sum(