POJ1664_递归问题中的排列组合思想(个人看法)

题目为:POJ1664

思路:
使用函数apple(m,n)表示将m个苹果放入n个盘子。

首先考虑问题边界(递归出口): 当没有苹果或者只有一个盘子的时候,只有一种摆放方法所以apple(0,n)=apple(m,1)=1;

然后考虑一般关系式:
①当苹果数量<盘子数量时问题可以转化为将m个苹果放入m个盘子中,即apple(m,n)=apple(m,m);

②当苹果数量>=盘子数量时,问题可以分解为两个子问题:所有盘子都有苹果or有盘子为空。前者即先将所有盘子放入一个苹果再分剩下的苹果到n个盘子中;后者可以表示为将m个苹果放入n-1个盘子中。即apple(m,n)=apple(m,n-1)+apple(m-n,n)。
(理解点: 有盘子为空只放出来了一个空盘子,但实际上apple(m,n-1)这个子问题也有其“有盘子为空”的子问题,这样一直细分子问题就解决有多个空盘子的问题。) 

那么,其中我们将递归问题分解为比它小的子问题时,需要考虑到每一种子问题的情况,不能漏掉任何一种子问题,这就有点像高中我们写“排列组合”数学题时需要考虑的情景。

AC代码如下:

#include<iostream>
#include<algorithm>
#include<cmath>
#include<cstring>
using namespace std;
typedef long long ll;
ll apple(ll m,ll n){
    if(m==0)
        return 1;
    if(n==1)
        return 1;
    if(n>m)
        return apple(m,m);
    else{
        return apple(m-n,n)+apple(m,n-1);
    }
}

int main(){
    ll t;
    cin>>t;
    while(t--){
        ll m,n;
        cin>>m>>n;
        ll ans=apple(m,n);
        cout<<ans<<endl;
    }

}

附:较为官方的递归定义

程序调用自身的编程技巧称为递归。

它通常把一个大型的复杂问题转化为一个与原问题相似的规模较小的问题来解决,可以极大的减少代码量。 使用递归要注意的4条基本法则:

1)基本情形:必须总要有某些基准情形,它无需递归就能解出。

2)要有进展:对于那些需要递归求解的情形,每一次递归调用都必须要使状态朝向一种基本情形推进。

3)设计法则:假设所有的递归调用都能运行。

4)合成效益法则:在求解一个问题的同一实例时,切勿在不同的递归调用中做重复性的工作。

最后一点涉及到记忆化递归,将已经算过的东西记录下来,避免重复计算,和搜索中的记忆化剪枝思想类似。

  • 11
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
题目描述 给出一个$n\times m$的矩阵,每个位置上有一个非负整数,代表这个位置的海拔高度。一开始时,有一个人站在其一个位置上。这个人可以向上、下、左、右四个方向移动,但是只能移动到海拔高度比当前位置低或者相等的位置上。一次移动只能移动一个单位长度。定义一个位置为“山顶”,当且仅当从这个位置开始移动,可以一直走到海拔高度比它低的位置上。请问,这个矩阵最多有多少个“山顶”? 输入格式 第一行两个整数,分别表示$n$和$m$。 接下来$n$行,每行$m$个整数,表示整个矩阵。 输出格式 输出一个整数,表示最多有多少个“山顶”。 样例输入 4 4 3 2 1 4 2 3 4 3 5 6 7 8 4 5 6 7 样例输出 5 算法1 (归dp) $O(nm)$ 对于这道题,我们可以使用归DP来解决,用$f(i,j)$表示以$(i,j)$为起点的路径最大长度,那么最后的答案就是所有$f(i,j)$的最大值。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码 算法2 (动态规划) $O(nm)$ 动态规划的思路与归DP类似,只不过转移方程和实现方式有所不同。 状态转移方程如下: $$ f(i,j)=\max f(x,y)+1(x,y)是(i,j)的下一个满足条件的位置 $$ 注意:这里的状态转移方程的$x,y$是在枚举四个方向时得到的下一个位置,即: - 向上:$(i-1,j)$ - 向下:$(i+1,j)$ - 向左:$(i,j-1)$ - 向右:$(i,j+1)$ 实现过程需要注意以下几点: - 每个点都需要搜一遍,因此需要用双重for循环来枚举每个起点; - 对于已经搜索过的点,需要用一个数组$vis$来记录,防止重复搜索; - 在进行状态转移时,需要判断移动后的点是否满足条件。 时间复杂度 状态数为$O(nm)$,每个状态转移的时间复杂度为$O(1)$,因此总时间复杂度为$O(nm)$。 参考文献 C++ 代码

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值