[P1216 USACO1.5][IOI1994]数字三角形 Number Triangles - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
题目描述
观察下面的数字金字塔。
写一个程序来查找从最高点到底部任意处结束的路径,使路径经过数字的和最大。每一步可以走到左下方的点也可以到达右下方的点。
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
在上面的样例中,从 7→3→8→7→5 的路径产生了最大
输入格式
第一个行一个正整数 r ,表示行的数目。
后面每行为这个数字金字塔特定行包含的整数。
输出格式
单独的一行,包含那个可能得到的最大的和。
输入输出样例
输入 #1
5
7
3 8
8 1 0
2 7 4 4
4 5 2 6 5
输出 #1
30
说明/提示
【数据范围】
对于 100% 的数据,1≤ r ≤1000,所有输入在 [0,100] 范围内。
因为题目的样例用贪心就能过,所以将样例稍作改编
输入 #2
5
7
3 8
8 1 0
2 4 4 7
4 5 2 6 5
输出 #2
28
这种做法其实是一种贪心的思想,来看它的定义:
我们会发现,即使在第四步选择了右边这个点,结果(7->8->1->4->6=26)比题目给出的答案7->8->0->7->6总和为28小.即题目在第三层就选择了小的0而不是1。
正是因为我们每次选择的时候只考虑当前的物品而没有考虑后面的物品,而产生的决策错误.
因此我们可以知道贪心策略的限制条件,每次选择局部最优解的时候,一定要保证局部最优策略不会对后续决策产生影响,如此才能使用贪心策略。
我们再来考虑一种方法,既然贪心行不通,我们不妨考虑通过搜索来获得它所有的结果.

然而,会发现:在每一层每个结点都有两种选择,选择左下或者右下,那么数字三角形有 2n-1条路线,我们既需要O(2n-1)的时间,又需要2n-1的空间,如果不进行剪枝当层数很大的时候这是行不通的。
我们考虑一下能不能把搜索优化。
首先,回到刚才那个想法,在第 4 层 4 和 4 相同的情况下到底计算机应该选择哪一个。此时,我们可以考虑让计算机分别对这两个结点进行搜索,搜索完再返回大的值,即我们要选择的结点。
接着,我们想这种想法可不可以在两个结点不相同的时候也使用。于是就有了我们从第一个结点开始,往下先对3和8进行搜索,3和8再分别对自己的子节点搜索…………搜索完后逐层向上返回最大值,这样使得计算机在每次决策的时候选择的都是最优的。

可惜,我们会发现,这个时间复杂度还是O(2^n-1),因为还是每个结点要对底下的两结点搜索。
NOT GIVE UP
我们反思一下刚才的递归算法
我们先把图简化,我们看第三层,是不是(2,1)和(2,2)均要执行一次solve(3,2)这个函数,即(3,2)这个点将要被执行两次。
可能我们会认为,重复算一两个数影响不大,但是,我们以此类推,会发现(3,2)的子节点也会被搜索多次,这样,当层数很多时重复搜索的次数会很多,导致了时间的浪费(这就是-----重叠子问题)
我们想想,有什么方法可以防止重复搜索。
我们考虑用一个二维数组 d[ i ][ j ] 来记录这个递归的返回值。
int solve(int i,int j)
{
d[i][j]=a[i][j]+(i==layer?0:max(solve(i+1,j),solve(i+1,j+1)));
return d[i][j];
}
记录好了,应该如何让计算机明白“已经计算过了,不用再计算了”?
int solve(int i,int j)
{
if(d[i][j]>=0) return d[i][j];
d[i][j]=a[i][j]+(i==layer?0:max(solve(i+1,j),solve(i+1,j+1)));
return d[i][j];
}
当 d[i][j] == 0 时表示已经计算过了,如果 d 数组初值为 0,每次搜索都会直接返回,所以我们还需要给d数组赋上初值,即在int main()函数中加入这样一句。
memset(d,-1,sizeof d);

这就是记忆化搜索
由于我们储存了每个结点递归的返回值,我们可以保证每个结点只被递归计算一次。所以时间复杂度是O(n2),从2n~n2这是一个巨大的优化。
int solve(int i,int j)
{
if(d[i][j]>=0) return d[i][j];
d[i][j]=a[i][j]+(i==layer?0:max(solve(i+1,j),solve(i+1,j+1)));
return d[i][j];
}
我们来考虑d(i,j)这个数组的意义,可以发现d(i,j)表示这个位置出发能得到的最大和(包括本身)。
我们把d(i,j)当成一个函数,那么原问题就可以是求解d(1,1)这个值,即代入下面这个数学函数。

这样,我们就引出了今天的主角-----动态规划
什么是动态规划?
简单来说,dp是具有递推形式的记忆化搜索,其核心思路是将大问题转化为可以重复被调用最优解的子问题并最终递推出题目整体最优解。
在动态规划的概念里,我们把d(i,j)定义为一个”状态”,而这个方程就是所谓的”状态转移方程”。
而这个状态体现的从 ( i , j ) 出发的最大总和,正是”最优子结构”,即”全局最优解包含局部最优解”,这就有效解决了贪心算法中(局部最优解不一定是整体最优解)的问题。
在上面的记忆化搜索中,我们求解的方式是从方程左边到方程右边,而动态规划正相反,从右边推出左边。

最后呈现的正是计算机决策的路径。这一方法被我们称为”递推”。
动态规划的要素:1.初始状态 2.递推关系公式
动态规划的特征:1.最优子结构 2.无后效性 3.重复子问题
最优子结构:问题的最优解包含子问题的最优解
无后效性:某阶段状态只关心前面阶段的状态值。一旦确定,就不受之后阶段的决策影响。
重复子问题: 不同的决策序列,到达某个相同的阶段时,可能会产生重复的状态。
我需要在客户计算机上运行Ruby应用程序。通常需要几天才能完成(复制大备份文件)。问题是如果启用sleep,它会中断应用程序。否则,计算机将持续运行数周,直到我下次访问为止。有什么方法可以防止执行期间休眠并让Windows在执行后休眠吗?欢迎任何疯狂的想法;-) 最佳答案 Here建议使用SetThreadExecutionStateWinAPI函数,使应用程序能够通知系统它正在使用中,从而防止系统在应用程序运行时进入休眠状态或关闭显示。像这样的东西:require'Win32API'ES_AWAYMODE_REQUIRED=0x0
我有一个模型:classItem项目有一个属性“商店”基于存储的值,我希望Item对象对特定方法具有不同的行为。Rails中是否有针对此的通用设计模式?如果方法中没有大的if-else语句,这是如何干净利落地完成的? 最佳答案 通常通过Single-TableInheritance. 关于ruby-on-rails-Rails-子类化模型的设计模式是什么?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.co
Rackup通过Rack的默认处理程序成功运行任何Rack应用程序。例如:classRackAppdefcall(environment)['200',{'Content-Type'=>'text/html'},["Helloworld"]]endendrunRackApp.new但是当最后一行更改为使用Rack的内置CGI处理程序时,rackup给出“NoMethodErrorat/undefinedmethod`call'fornil:NilClass”:Rack::Handler::CGI.runRackApp.newRack的其他内置处理程序也提出了同样的反对意见。例如Rack
我想用ruby编写一个小的命令行实用程序并将其作为gem分发。我知道安装后,Guard、Sass和Thor等某些gem可以从命令行自行运行。为了让gem像二进制文件一样可用,我需要在我的gemspec中指定什么。 最佳答案 Gem::Specification.newdo|s|...s.executable='name_of_executable'...endhttp://docs.rubygems.org/read/chapter/20 关于ruby-在Ruby中编写命令行实用程序
我构建了两个需要相互通信和发送文件的Rails应用程序。例如,一个Rails应用程序会发送请求以查看其他应用程序数据库中的表。然后另一个应用程序将呈现该表的json并将其发回。我还希望一个应用程序将存储在其公共(public)目录中的文本文件发送到另一个应用程序的公共(public)目录。我从来没有做过这样的事情,所以我什至不知道从哪里开始。任何帮助,将不胜感激。谢谢! 最佳答案 无论Rails是什么,几乎所有Web应用程序都有您的要求,大多数现代Web应用程序都需要相互通信。但是有一个小小的理解需要你坚持下去,网站不应直接访问彼此
我尝试运行2.x应用程序。我使用rvm并为此应用程序设置其他版本的ruby:$rvmuseree-1.8.7-head我尝试运行服务器,然后出现很多错误:$script/serverNOTE:Gem.source_indexisdeprecated,useSpecification.Itwillberemovedonorafter2011-11-01.Gem.source_indexcalledfrom/Users/serg/rails_projects_terminal/work_proj/spohelp/config/../vendor/rails/railties/lib/r
刚入门rails,开始慢慢理解。有人可以解释或给我一些关于在application_controller中编码的好处或时间和原因的想法吗?有哪些用例。您如何为Rails应用程序使用应用程序Controller?我不想在那里放太多代码,因为据我了解,每个请求都会调用此Controller。这是真的? 最佳答案 ApplicationController实际上是您应用程序中的每个其他Controller都将从中继承的类(尽管这不是强制性的)。我同意不要用太多代码弄乱它并保持干净整洁的态度,尽管在某些情况下ApplicationContr
我将应用程序升级到Rails4,一切正常。我可以登录并转到我的编辑页面。也更新了观点。使用标准View时,用户会更新。但是当我添加例如字段:name时,它不会在表单中更新。使用devise3.1.1和gem'protected_attributes'我需要在设备或数据库上运行某种更新命令吗?我也搜索过这个地方,找到了许多不同的解决方案,但没有一个会更新我的用户字段。我没有添加任何自定义字段。 最佳答案 如果您想允许额外的参数,您可以在ApplicationController中使用beforefilter,因为Rails4将参数
我是一个Rails初学者,但我想从我的RailsView(html.haml文件)中查看Ruby变量的内容。我试图在ruby中打印出变量(认为它会在终端中出现),但没有得到任何结果。有什么建议吗?我知道Rails调试器,但更喜欢使用inspect来打印我的变量。 最佳答案 您可以在View中使用puts方法将信息输出到服务器控制台。您应该能够在View中的任何位置使用Haml执行以下操作:-puts@my_variable.inspect 关于ruby-on-rails-如何在我的R
如何检查Ruby文件是否是通过“require”或“load”导入的,而不是简单地从命令行执行的?例如:foo.rb的内容:puts"Hello"bar.rb的内容require'foo'输出:$./foo.rbHello$./bar.rbHello基本上,我想调用bar.rb以不执行puts调用。 最佳答案 将foo.rb改为:if__FILE__==$0puts"Hello"end检查__FILE__-当前ruby文件的名称-与$0-正在运行的脚本的名称。 关于ruby-检查是否