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

题目链接:https://www.luogu.org/problem/show?pid=1437#sub

     http://codevs.cn/problem/1257/

 

不得不说,这个题非常的恶心,在初次拿到题后我的思路是暴力,思索之后我还是只有暴力,想到最后我还是暴力,当然暴力的方法就是智者见智的了;

在看了题解的思路后,我尝试着去打着到题,结果,完美WA掉。。。然后我就WA了接近一周时间,最后浏览了各种博客才勉强A了这道恶心的dp

很多大佬的博客对于这道题的解法都是要转90度然后才定义dp,说实话,我觉得那种方法略偏麻烦,完全可以不处理原图直接dp

对于这道题,第一件事就是要怎么去看这张图

14   15   4   3   23

    33   33   76   2

    2   13   11

      22   23

        31

这是题干里的图,当然在看这张图的时候,要稍微换个方式

14  15  4    3  23

33  33  76  2

2    13  11

22  23

31

这张图有没有很熟悉,想起了啥???我反正是想起了dp入门题里面一道类似的三角形的题,不过图倒过来了

当然就算想起来了也没有大用处,因为这不是重点。

把图变成这样主要是为了方便分析滴

我们来定义一个数组吧:(这才是重点)

定义数组dp[i][j][k]表示第i列第j块砖时已经一共取了k块砖  (也可以理解为,第i列取了j块砖且一共去了k块砖)

好了定义出来了我相信这个方程其实也很容易搞出来了,要注意的是我们要从第n列往回找,不要问我为啥,因为不这样找,你的dp数组就会有些地方没有值

动态转移方程

----------------------------------------------------------------------------------------------------------------------------------

--                                                    --

--dp[i][j][k]=max(dp[ i ][ j ][ k ],dp[ i+1 ][ v ][ k-j ]+sum[ j ][ i ]); --

--                                                    -- 

----------------------------------------------------------------------------------------------------------------------------------

然后我就来解释一下这个动态转移方程

*dp[i+1][v][k-j]中,v是从j-1到m,因为在第i列取了j个砖头,所以第i+1列至少要去j-1个(通过题中条件,取第i,j块必须先取i-1, j+1和i-1,j)

 然后在第i列是一共取k个,所以第i+1列肯定是一共取了k-j个

*sum[j][i]是表示从a[1][i]到a[j][i]的值的和。因为你取第i列第j块,肯定是在第i列上要把前j个取完的

把这些个一想通,这题就明了了,一个预处理sum[][],在来个4重循环跑个dp数组就完了

唯一还值得注意就是ans是可能存在dp数组的任何位置,所以一边动态转移,一边要比较ans

题不难,就看细心了

 1 #include
 2 #include
 3 #include
 4 #include
 5 #include
 6 #include
 7 #define maxn 55
 8 #define maxm 1300
 9 using namespace std;
10 
11 int n,m,ans;                    
12 int f[maxn][maxn][maxm];            
13 int val[maxn][maxn];            
14 
15 int main()
16 {
17     scanf("%d%d",&n,&m);
18     memset(f,-1,sizeof(f));
19     for(int i=1;i<=n;i++)
20     {
21         for(int j=1;j<=n-i+1;j++)
22         {
23             scanf("%d",&val[i][j]);
24             val[i][j]+=val[i-1][j];
25         }
26     }
27     f[n][1][1]=val[1][n];
28     for(int i=n-1;i>=1;i--)
29      for(int j=0;j<=n-i+1;j++)
30       for(int k=j;k<=min(m,(n-i+1)*(n-i+2)/2);k++)
31       //第i列的时候,最多取了p*(p+1)/2块砖头 p=n-i+1
32       {
33           for(int v=max(j-1,0);v<=n-i;v++)//i+1列最多就n-i块砖 
34           {
35             if(f[i+1][v][k-j]!=-1&&f[i+1][v][k-j]+val[j][i]>f[i][j][k])
36                 f[i][j][k]=f[i+1][v][k-j]+val[j][i];         
37           }
38           ans=max(ans,f[i][j][k]);
39       }
40     printf("%d",ans);
41 }
View Code

 

你可能感兴趣的:([洛谷1437&Codevs1257]敲砖块<恶心的dp>)