找钱问题(递归学习的不断进步)

本文探讨了一道购票顺序问题,通过逐步优化算法,从递归到动态规划的方法,最终实现了高效求解不同购票组合数量的目标。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

公园票价为5角。假设每位游客只持有两种币值的货币:5角、1元。
再假设持有5角的有m人,持有1元的有n人。
由于特殊情况,开始的时候,售票员没有零钱可找。

我们想知道这m+n名游客以什么样的顺序购票则可以顺利完成购票过程。
显然,m < n的时候,无论如何都不能完成;
m>=n的时候,有些情况也不行。比如,第一个购票的乘客就持有1元。
请计算出这m+n名游客所有可能顺利完成购票的不同情况的组合数目。

注意:只关心5角和1元交替出现的次序的不同排列,持有同样币值的两名游客交换位置并不算做一种新的情况来计数。

分析:

看到题的第一眼就知道使用递归解决问题了。

使用老套路, 分别模拟,每次购票,每次购票有可能是带1元的也有可能是带5角的。

分析,递归需要的参数,需要有一个售票员参数,售票员参数,是售票员手里面一开始有几张5角。


public class Main {
	
   
    public static void main(String[] args) {
    	 System.out.println(f(1,2,0));
    	 System.out.println(f(2,2,0));
         System.out.println(f(3,2,0));
         System.out.println(f(5,3,0));
    }
    
    
    //m 持有5角的人
    //n 持有1元的人
    //c 售票员手里面有几张5角
    public static int f(int m,int n,int c){
    	
    	//当售票员手里的5角不够用时返回 0 代表该次模拟 不成功
    	if(c<0)return 0;
    	
    	//当两种顾客都服务完了, 返回 1 
    	if(m==0&&n==0)return 1;
    	
    	//当5角钱的顾客服务完了,去服务1元钱的
    	if(m==0)return f(m,n-1,c-1);
    	
    	//当1元钱的服务完了,去服务5角钱的
    	if(n==0)return f(m-1,n,c+1);
    	
    return f(m-1,n,c+1) + f(m,n-1,c-1);
    }
   

}



使用小问题去验证递归正确性。

当 5 角的人数为 1   , 1元的人数 为 2 

程序无论如何运行都会发生零钱不够找。


当 5 角的人数为 2   , 1元的人数 为 2

可能发生的排队情况  5 1 5 1、 5 5 1 1 

当 5 角的人数为 3   , 1元的人数 为 2

可能发生的排队情况  5 5 5 1 1、5 5 1 5 1、5 5 1 1 5、5 1 5 5 1、5 1 5 1 5 。


改进递归。


public class Main {
	
   
    public static void main(String[] args) {
    	 System.out.println(f(1,2,0));
    	 System.out.println(f(2,2,0));
         System.out.println(f(3,2,0));
         System.out.println(f(5,3,0));
    }
    
    
    //m 持有5角的人
    //n 持有1元的人
    //c 售票员手里面有几张5角
    public static int f(int m,int n,int c){
    	//在所有变化过程中,一旦,m+c<n ,零钱怎么都不够找
    	if(m+c<n)return 0;
    	
    	//当5角钱的顾客服务完了,只有一种方式,就是服务剩余的全部1元顾客
    	if(m==0)return 1;
    	
    	//与上同理
    	if(n==0)return 1;
    	
    	int r = f(m-1,n,c+1);
    	//只有当C大于零的时候才去尝试服务1元客户
    	if(c>0){
    		r+= f(m,n-1,c-1);
    	}
    return r;
    }
   

}

换个角度思考,再次改进递归。

之前是使用的常规角度去思考该问题,需要一个售票员参数,

之前考虑的是,从前往后对 5 角客户  1 元客户 进行服务,现在,考虑从后面开始


public class Main {
   
    public static void main(String[] args) {
    	 System.out.println(f(1,2));
    	 System.out.println(f(2,2));
         System.out.println(f(3,2));
         System.out.println(f(5,3));
    }
    
    
    public static int f(int m,int n){
    	//每次往前推,都要考虑,此时此刻,5角 的客户, 还够不够找 1 元的
    	if(m<n)return 0;
    	
    	//一元的没有了
    	if(n==0)return 1;
    	
    	//5角的还剩下一个 -》》既然可以通过 m<n 的检验  说明此时  n应该 有 1 个或者0个。
    	//只有一种结果,先花 5 角的 再花 1 元的
    	if(m==1)return 1;
    	
    	
    	//假设最后一个服务的是5角客户,减少一个5角客户,计算m-1个5角用户 可能出现的排列种数,
    	//假设最后一个服务的是1元客户   减少一个1角客户,计算n-1个1元用户 可能出现的排列种数,
    return f(m-1,n) + f(m,n-1);
    }	
   

}

递归精简完了,动态规划。

public class Main {
	
   
    public static void main(String[] args) {
    	 System.out.println(f(1,2));
    	 System.out.println(f(2,2));
         System.out.println(f(3,2));
         System.out.println(f(5,3));
    }
    
   
    
    
    public static int f(int m,int n){
    	int[][] dp = new int[m+1][n+1];
    	for(int i=0;i<m+1;i++){
    		if(i>=0)dp[i][0] = 1;
    	}
    	for(int i=0;i<n+1;i++){
    		if(1>=i)dp[1][i] = 1;
    	}
    	for(int i=1;i<m+1;i++){
    		for(int j=1;j<n+1;j++){
        		if(i>=j){
        			dp[i][j] = dp[i-1][j] + dp[i][j-1];
        		}
        	}
    	}
    return dp[m][n];
    }
   

}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SUNbrightness

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值