zoj 1163 The Staircases 动态规划(dp)
2011-03-12 23:42
477 查看
这道题目通过dp来解决,而且有许多解决方法,它们在时间和空间上的复杂度都不相同。让我们直接开始看这些解决方案:
当然我还是使用dp的,只是转化方程比较土鳖一点而已。考虑两个参数积木数N,台阶数S(有几级台阶),那么可以知道:f(N,S) = f(N-S, S-1) + f(N-2S, S-1) + f(N-3S, S-1) + .... + f(N-kS, S-1)。原理就是说考虑第一级的高度,如果为一,那么我们去掉每一级的最底下的那块积木,那么就剩下S-1级了,因为第一级没有了。同理如果高度为2,为3,。。。
这个方法可以搞定,而且我的代码也成功AC了。然后洋洋得意了一阵子,发现居然还有其他方法,而且相比之下,我这个方法就像是从乡下来的土鳖,真是令人丧气。
最高级的台阶正好高度为h,那么也就是剩下的N-h块积木在h-1的情况
最高级的台阶高度小于h,这个你懂的
而题目要求得到N的所有摆放方式,那么就是f(N,N)-1,注意这里的-1表示单独的一列,也就是只有一级台阶,这个是题目要求摒弃的。当然dp还有一个事情要确定就是初始条件,根据题意以及前面所说的定义方式,f(0,i)=1。然后就可以得到类似下面的代码了。
注意这个题目的另外一个陷阱,就是用long会溢出,你可以自己把N接近500的答案打出开看看,发现32位是不够的。好了,大家看到这里基本可以回去开心的code,然后帅气的AC了。
什么?你还不满足?
那好,我还有更好的解决方案。
好的,下面隆重介绍经过我研究了n天之后才完全搞明白的方案(不要bs我笨)。这个方案是对方案二的空间优化,优化方法一般也称为滚动数组,将方案二中的数组m[501][501]优化为m[501],也就是从O(N2)优化为O(N)。
考虑状态转移公式:f(N, h) = f(N-h, h-1) + f(N, h-1)。 我们可以发现相对应每个h的计算,都只是用到了h-1的数值,而与其他高度h的数值完全没有关系。参照方案二的代码,也就是说每一列的数值只是与前一列的的数值相关(j和j-1列)。
为了看的更加清楚,我们将二维数组m的两个维度交换一下(也就是i和j交换),状态方程就变为f(h, N) = f(h-1, N-h) + f(h-1, N),变成如下的形式:
现在要压缩m数组了,考虑在计算i行时,我们只需要i-1行的数值,而在i行计算结束之后i-1行的数据只需要留下m[i-1][i-1]就可以了(这是N=i-1的解)。现在我们使用一维数组m[501],同时假设,i-1行的数据已经搞定,整行存在m[501]这个一维数组中,那么计算i行的时候如果将内循环改为从大到小循环,情况会如何呢?这个时候可以发现,每次计算相应下标为j的值时,所使用的下标小于j的值,正是之前存在数组中的i-1行的数据,也就是说它依然是正确的。
现在需要解决另外一个问题,数据覆盖的问题,我们可以看到如果j从500循环到1,那么整个数组都被覆盖了,显然这是不行的,因为小于i的解没有存下来。所以这里的循环应该是从500到i,而下标小于i(比如为k)的数值正好对应了N=k的解。
证明:初始状态,m[1]就是N=1的解。递归转移:计算第i行的时候,观察在j<=i的时候,也就是内循环中的else分句,m[i][j] = m[i-1][j],如果i-1<=j,依然有m[i-1][j] = m[i-2][j],所以最终可以得到m[i][j] = m[j][j],也就是N=j的解,而这个解正好存在下标为j的位置上。因此这个计算依然是正确的。
如果难以理解上面两段,可以去http://www.oiers.cn/pack/P01.html看看其中的空间复杂度优化,我就是在这里面学习的。同时参照下面的代码,希望诸位可以耐心看懂,牢记在心。
方案一:
这是我最初研究出来的方案,是我本人当前智力能力所能想出来的最佳方案。虽然这个方案很笨,但是我相信会有人和我一样这么想的。当然我还是使用dp的,只是转化方程比较土鳖一点而已。考虑两个参数积木数N,台阶数S(有几级台阶),那么可以知道:f(N,S) = f(N-S, S-1) + f(N-2S, S-1) + f(N-3S, S-1) + .... + f(N-kS, S-1)。原理就是说考虑第一级的高度,如果为一,那么我们去掉每一级的最底下的那块积木,那么就剩下S-1级了,因为第一级没有了。同理如果高度为2,为3,。。。
这个方法可以搞定,而且我的代码也成功AC了。然后洋洋得意了一阵子,发现居然还有其他方法,而且相比之下,我这个方法就像是从乡下来的土鳖,真是令人丧气。
方案二:
网上可以搜到的主流解法,也是大部分人可以到达的境界(为什么我到不了呢?郁闷)。考虑最高级的台阶,也就是最右边一列的台阶,假设它的高度不大于h(注意,不是等于h)。那么我们可以得到状态转移方程:f(N, h) = f(N-h, h-1) + f(N, h-1),什么意思呢?就是说,计算N,h的过程可以拆分为两种情况:最高级的台阶正好高度为h,那么也就是剩下的N-h块积木在h-1的情况
最高级的台阶高度小于h,这个你懂的
而题目要求得到N的所有摆放方式,那么就是f(N,N)-1,注意这里的-1表示单独的一列,也就是只有一级台阶,这个是题目要求摒弃的。当然dp还有一个事情要确定就是初始条件,根据题意以及前面所说的定义方式,f(0,i)=1。然后就可以得到类似下面的代码了。
#include<stdio.h> #include<iostream> using namespace std; int main() { #ifndef ONLINE_JUDGE freopen("input.txt", "rt", stdin); freopen("output.txt", "wt", stdout); #endif double m[501][501]; for(int i=0;i<501;i++) { /*没研究到底可不可以去掉,反正去掉的可以AC for(int j=0;j<501;j++) { m[i][j] = 0; }*/ m[0][i] = 1; } for(int i=1;i<=500;i++) { for(int j=1;j<=500;j++) { if(i>=j) { m[i][j] = m[i-j][j-1]+m[i][j-1]; }else { m[i][j] = m[i][j-1]; } } } int N; cin >> N; while(N) { printf("%0.0lf/n",m -1); cin >>N; } }
注意这个题目的另外一个陷阱,就是用long会溢出,你可以自己把N接近500的答案打出开看看,发现32位是不够的。好了,大家看到这里基本可以回去开心的code,然后帅气的AC了。
什么?你还不满足?
那好,我还有更好的解决方案。
方案三:
是的,这是一个更加好的解决方案。但是,它做的只是空间的优化,同时对AC基本没有影响。如果你要赶紧去陪mm压马路的话那就Alt+F4吧。好的,下面隆重介绍经过我研究了n天之后才完全搞明白的方案(不要bs我笨)。这个方案是对方案二的空间优化,优化方法一般也称为滚动数组,将方案二中的数组m[501][501]优化为m[501],也就是从O(N2)优化为O(N)。
考虑状态转移公式:f(N, h) = f(N-h, h-1) + f(N, h-1)。 我们可以发现相对应每个h的计算,都只是用到了h-1的数值,而与其他高度h的数值完全没有关系。参照方案二的代码,也就是说每一列的数值只是与前一列的的数值相关(j和j-1列)。
为了看的更加清楚,我们将二维数组m的两个维度交换一下(也就是i和j交换),状态方程就变为f(h, N) = f(h-1, N-h) + f(h-1, N),变成如下的形式:
double m[501][501]; for(int i=0;i<501;i++) { m[i][0] = 1; } for(int i=1;i<=500;i++) { for(int j=1;j<=500;j++) { if(j>=i) { m[i][j] = m[i-1][j-i] + m[i-1][j];//两个维度交换了 }else { m[i][j] = m[i-1][j]; } } }
现在要压缩m数组了,考虑在计算i行时,我们只需要i-1行的数值,而在i行计算结束之后i-1行的数据只需要留下m[i-1][i-1]就可以了(这是N=i-1的解)。现在我们使用一维数组m[501],同时假设,i-1行的数据已经搞定,整行存在m[501]这个一维数组中,那么计算i行的时候如果将内循环改为从大到小循环,情况会如何呢?这个时候可以发现,每次计算相应下标为j的值时,所使用的下标小于j的值,正是之前存在数组中的i-1行的数据,也就是说它依然是正确的。
现在需要解决另外一个问题,数据覆盖的问题,我们可以看到如果j从500循环到1,那么整个数组都被覆盖了,显然这是不行的,因为小于i的解没有存下来。所以这里的循环应该是从500到i,而下标小于i(比如为k)的数值正好对应了N=k的解。
证明:初始状态,m[1]就是N=1的解。递归转移:计算第i行的时候,观察在j<=i的时候,也就是内循环中的else分句,m[i][j] = m[i-1][j],如果i-1<=j,依然有m[i-1][j] = m[i-2][j],所以最终可以得到m[i][j] = m[j][j],也就是N=j的解,而这个解正好存在下标为j的位置上。因此这个计算依然是正确的。
如果难以理解上面两段,可以去http://www.oiers.cn/pack/P01.html看看其中的空间复杂度优化,我就是在这里面学习的。同时参照下面的代码,希望诸位可以耐心看懂,牢记在心。
#include<stdio.h> #include<iostream> using namespace std; int main() { #ifndef ONLINE_JUDGE freopen("input.txt", "rt", stdin); freopen("output.txt", "wt", stdout); #endif double m[501]; m[0] = 1; for(int i=1;i<501;i++) m[i] = 0; for(int i=1;i<501;i++) { for(int j=500;j>=i;j--) { m[j] = m[j-i] + m[j]; } } int N; cin >> N; while(N) { printf("%0.0lf/n",m -1); cin >>N; } }
相关文章推荐
- POJ 1163 The Triangle & POJ 3176 Cow Bowling(DP动态规划)
- zoj1163 The Staircases (简单dp)
- zoj1163 The Staircases(DP)
- poj-1163-The Triangle-动态规划dp
- zoj1163 The Staircases(基础dp)
- poj 动态规划DP - 1163 The Triangle
- POJ 1163 DP 动态规划
- CQBZOJ - 2916 学生宿舍 动态规划(DP) 重庆一中高2018级竞赛班第十次测试 2016.9.16 Problem 1
- 【原】 POJ 1163 The Triangle 三角形最大路径 动态规划 解题报告
- openjudge 最大子矩阵 (DP 动态规划)
- ZOJ - 3822 Domination (概率DP)
- hdu1421搬寝室DP(ACM题目,动态规划)
- zoj 3288 Domination (概率dp)
- hdu 1087 Super Jumping! Jumping! Jumping!(动态规划DP)
- ZOJ - 3822 Domination (DP)
- ZOJ 3822 Domination(DP)
- poj 3616 Milking Time ---DP(带权重的区间动态规划)
- ZOJ 3822 Domination (三维概率DP)
- 动态规划(dp)算法总结
- HDU 5464 Clarke and problem(dp 动态规划)——BestCoder Round #56(div.1 div.2)