[洛谷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
题不难,就看细心了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include<cstdio> 2 #include<cstring> 3 #include<algorithm> 4 #include<iostream> 5 #include<cstdlib> 6 #include<cmath> 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 }