数字三角形 题解

题目传送门

听说这是经典dp

于是乎我用了贪心 记忆搜索

在贴代码之前还是讲一讲吧。

- dp、分治、递归、递推、搜索 的关系

很简单,没有关系 dp 是递推,是搜索的改版,是递归的进化体,是(在死亡的边缘大鹏展翅)分治的体现。

- 详解 水题解 数字三角形

首先排除贪心。

为什么?

看一眼样例吧,的确木有贪心策略。

但是,我们可以使用搜索愉快地玩耍。

那么具体怎么搜索呢?(好问题,我也不知道)

#include<bits/stdc++.h>
using namespace std;
int r,a[1010][1010],i,j,ans;
void dfs(int h,int l,int s){
   if(h==r){
   	ans=max(ans,s);
       return;
   }
   dfs(h+1,l,s+a[h][l]);
   dfs(h+1,l+1,s+a[h][l]);
}
int main(){
   memset(dp,-1,sizeof(dp));
   cin>>r;
   for(i=0;i<r;i++) for(j=0;j<=i;j++) cin>>a[i][j];
   dfs(0,0,0);
   return 0;
}

这就是最水的深搜(爆搜),不给注释了

不用试了,我交过,TLE*4。


神奇的记忆化

这也很简单。首先记忆化一定满足:

  • 函数返回值仅与其参数有关
  • 相同参数下的返回值相同
  • 以返回值传递结果
  • 有一个记忆数组(滑稽)

辣么,我们的答案就会要以返回值传递。

具体的思路也很简单,只要 洗洗睡吧其实要改很多 调整一下思路,你就会发现:

答案居然是 max(左边路的最大分数,右边路的最大分数)+自己的数

如果你还要问为什么,我也会 严肃 地回答你:

这里的左边路、右边路不是指一直向左走向右走,是本次左走之后所有的可以走的路(右边也一样)。

所以这也是显而易见的

所以就会很简单的开始你的记忆化。

实际上三个参数的记忆化很烦的(动不动爆空间),但是,因为最终答案是以返回值传递的,所以最初爆搜代码的参数 s 就可以丢掉了。

#include<bits/stdc++.h>
using namespace std;
int r,a[1010][1010],i,j,dp[1010][1010];
/*
* dp是记忆数组。
*
* h和l表示行、列
*/
int dfs(int h,int l){
   if(dp[h][l]>-1) return dp[h][l];
   // 如果记忆过就直接返回
   if(h==r) dp[h][l]=a[h][l];
   // 如果搜到底层就返回数字
   else dp[h][l]=max(dfs(h+1,l),dfs(h+1,l+1))+a[h][l];
   // 伪状态转移方程
   return dp[h][l];
   //快乐结束
}
int main(){
   memset(dp,-1,sizeof(dp));//初始化,便于判断
   cin>>r;
   for(i=0;i<r;i++) for(j=0;j<=i;j++) cin>>a[i][j];
   //输入完成
   cout<<dfs(0,0);
   return 0;
   //标准结局(此结局非彼结局)
}
/*
* 题目数据不是很大,于是乎木有输入输出优化
*
* 码风不好我也没办法 QAQ
*/

辣么这道题就可以AC了,所以我也不写 dp 代码惹 其实是我不会dp

最后贴一张图,手动去除背景水印,所以比较亮(请佩戴墨镜)

这是一个算法区分图,里面的高级名词可以手动百度~


管理求过,第二次发题解(第一次发的题解误删了QAQ)