递归算法练习

本文深入解析了四个经典的算法问题,包括鸭子销售商的数学谜题、角谷猜想的实现、电话号码的字符组合以及复杂分配问题的逆向求解。通过详细的步骤说明和代码示例,展示了递归、回溯等算法的设计与应用。

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

1.一个人赶着鸭子去每个村庄卖,每经过一个村子卖去所赶鸭子的一半又一只。这样他经过了七个村子后还剩两只鸭子,问他出发时共赶多少只鸭子?经过每个村子卖出多少只鸭子?

题目分析:第7天卖完剩2只,则第六天卖完剩2*(2+1)只,第五天卖完剩2*(2*(2+1)+1)只...依次类推,用Fun(7)表示未卖之前的总数,相当于第0个村庄,那么Fun(6)表示路过第一个村庄,...依次类推,用F(0)表示路过第七个村庄,所以当m=0时,return 2,为该程序的出口。其他情况return 2*(Fun(n-1)+1);

算法结构:

      递归体:return 2*(Fun(n-1)+1)

代码实现:

package test;
/*
 * 一个人赶着鸭子去每个村庄卖,每经过一个村子卖去所赶鸭子的一半又一只。
 * 这样他经过了七个村子后还剩两只鸭子,问他出发时共赶多少只鸭子?
 * 经过每个村子卖出多少只鸭子?
 */
public class Test01 {
	public static void main(String[] args) {
		int sum = Duck(7);//调用方法
		System.out.println("总共有" + Duck(7) + "只鸭子");
		for (int i = 1; i <= 7; i++) {//利用for循环输出经过每个村子卖出鸭子的个数
			System.out.println("第" + i + "个村子卖出" + (sum / 2 + 1) + "只鸭子");
			sum = sum / 2 - 1;//剩余的鸭子数为前一天剩余的鸭子数的一半减1
		}
	}
	/*
	 * Duck为递归方法,参数n代表经过的村子个数
	 */
	public static int Duck(int n) {
		if (n == 0) {//0表示第七个村子
			return 2;
		} else {
			return 2 * (Duck(n - 1) + 1);
		}
	}
}

2.角谷定理。输入一个自然数,若为偶数,则把它除以2,若为奇数,则把它乘以3加1。经过如此有限次运算后,总可以得到自然数值1。求经过多少次可得到自然数1。

如:输入22,

输出 22 11 34 17 52 26 13 40 20 10 5 16 8 4 2 1

 STEP=16

题目分析:首先输入一个自然数n,调用函数gujiao(int n),将n的值传给形参。每次进行判断如果是偶数,则n=n/2,是奇数则     n=(n*3+1),递归调用gujiao(n)函数,直到n=1的时候,返回步数,调用结束。If(n==1){return step;}为出口,return gujiao(n)为函数体。

算法结构:

     出口:If(n==1){return step;}

     递归体:return gujiao(n)

代码实现:

package test;

import java.util.Scanner;
/*
 * 角谷定理。输入一个自然数,若为偶数,则把它除以2,若为奇数,
 * 则把它乘以3加1。经过如此有限次运算后,总可以得到自然数值1。
 * 求经过多少次可得到自然数1。
 */
public class Test02 {
	public static void main(String[] args) {
		System.out.println("请输入一个自然数:");
		Scanner sc = new Scanner(System.in);
		gujiao(sc.nextInt());
		System.out.println("\n" + "step=" + step);
	}

	static int step = 0;//静态方法定义经过的次数
	/*
	 * gujiao为递归方法,参数n代表自然数
	 */
	public static int gujiao(int n) {
		System.out.print(n + " ");
		step++;//没经过一次,次数加1
		while (true) {
			if (n == 1) {//递归出口
				return step;
			} else {
				if (n % 2 == 0) {
					n = n / 2;
				} else {
					n = n * 3 + 1;
				}
			}
			return gujiao(n);//递归体
		}
	}
}

3.电话号码对应的字符组合:在电话或者手机上,一个数字如2对应着字母ABC,7对应着PQRS。那么数字串27所对应的字符的可能组合就有3*4=12种(如AP,BR等)。现在输入一个3到11位长的电话号码,请打印出这个电话号码所对应的字符的所有可能组合和组合数。

题目分析:

     2对应的是abc  3对应的是def 4对应的是ghi 5对应的是jkl

    6对应的是mno 7对应的是pqrs 8对应的是tuv 9对应的是wxyz。

    2对应3种可能性  3对应3种可能性 4对应3种可能性 5对应3种可能性

    6对应3种可能性 7对应4种可能性 8对应3种可能性9对应4种可能性。

    利用数组将这些信息存储起来,在利用循环进行遍历。输入3到11位数字,输出组合情况,并且输出组合情况的总数

算法分析:

      int类型存放不下11位的数字,这里用String类型接收。根据题目要求,定义数组,用来存放字母。为了方便输出组合,把输入的数字转换为字符后,用数组来存放,再用回溯法实现组合输出.

代码实现:

package test;

import java.util.ArrayList;
import java.util.List;
import java.util.Scanner;
/**
 * 题目描述:
 * 电话号码对应的字符组合:在电话或者手机上,一个数字如2对应着字母ABC,7对应着PQRS。
 * 那么数字串27所对应的字符的可能组合就有3*4=12种(如AP,BR等)。
 * 现在输入一个3到11位长的电话号码,请打印出这个电话号码所对应的字符的所有可能组合和组合数。
 */
public class Test06 {
	static String[] str= new String[] {"0","1","abc","def",
				"ghi","jkl","mno","pqrs","tuv","wxyz"};//定义每个数字对应的字符
	public static void main(String[] args) {
		int sum=1;//定义变量统计组合数
		System.out.println("请输入3到11位的电话号码:");
		Scanner sc=new Scanner(System.in);
		String s=sc.next().toString();//将输入的数字转换为字符
		String[] s1 = s.split("");//将输入的一串数字分离,并赋给数组s1保存
        int len = s1.length;//输入数字长度
        for(int i=0;i<len;i++){ //统计组合数
            sum *= str[Integer.valueOf(s1[i])].length();
        }
        System.out.println("组合数sum = "+sum);
        System.out.println("可能的组合为:");
		List<String> x = letterCombinations(s);
		System.out.println(x.toString());
	}
	
	static StringBuffer sb = new StringBuffer();
	public static List<String> letterCombinations(String digits) {
		if (digits.length() == 0) {
			return null;
		}
		List<String> answer = new ArrayList<String>();	
		zuhe(digits , 0 , answer);//开始回溯
		return answer;
	}
	/*
	 * zuhe方法用于实现字母的每种可能的组合
	 */
	private static void zuhe(String digits , int n, List<String> answer) {
		if (n == digits.length()) {
			answer.add(sb.toString());
			return;
		}
		for (int i = 0; i < str[digits.charAt(n)-'0'].length(); i++) {
			sb.append(str[digits.charAt(n)-'0'].charAt(i));
			zuhe(digits, n + 1, answer);
			sb.deleteCharAt(sb.length() - 1);
		}
	}
}

4.日本著名数学游戏专家中村义作教授提出这样一个问题:父亲将2520个桔子分给六个儿子。分完 后父亲说:“老大将分给你的桔子的1/8给老二;老二拿到后连同原先的桔子分1/7给老三;老三拿到后连同原先的桔子分1/6给老四;老四拿到后连同原先的桔子分1/5给老五;老五拿到后连同原先的桔子分1/4给老六;老六拿到后连同原先的桔子分1/3给老大”。结果大家手中的桔子正好一样多。问六兄弟原来手中各有多少桔子?

题目分析:从数学的思维来分析,倒推:

总共2520,分到最后每人是2520/6=420 ;

老六拿到后连同原先的桔子分1/3给老大,所以老六没分给老大之前是420*3/2=630,分给老大的是630/3=210;

老大最后也是420,所以老大在分给老二后的橘子数为420-210=210;而老大将分给你的桔子的1/8给老二,故老大未分给老二前的橘子数为210*8/7=240; 老二拿到后连同原先的桔子分1/7给老三,设老二本身的橘子树为x,则(x+30)*6/7=420;老二本身的橘子数为460;

….

以此类推

代码实现:

package test;
/**
 * 日本著名数学游戏专家中村义作教授提出这样一个问题:
 * 父亲将2520个桔子分给六个儿子。分完 后父亲说:
 * “老大将分给你的桔子的1/8给老二;老二拿到后连同原先的桔子分1/7给老三;
 * 老三拿到后连同原先的桔子分1/6给老四;老四拿到后连同原先的桔子分1/5给老五;
 * 老五拿到后连同原先的桔子分1/4给老六;老六拿到后连同原先的桔子分1/3给老大”。
 * 结果大家手中的桔子正好一样多。问六兄弟原来手中各有多少桔子?
 */

public class Test04 {
	public static void main(String[] args) {
		digui();//调用方法
	}
	/*
	 * 构造方法
	 */
	public static void digui(){
	    int left = 0;//前一个给下一个的桔子数
		int avge=2520/6;//最终每个人手里的桔子个数
		int[] a=new int[6];//定义数组,分别代表每个儿子原来的桔子个数
		for (int i = 0; i < 6; i++)
	    {
	        if (0 == i)//求老大桔子数时,利用老六与老大桔子数量的关系求解
	        {
	           a[i] = (avge - 210)*(8 - i)/(7 - i) - left;
	           left = a[i] + left - (avge - 210);
	        }
	        else
	        { 
	           a[i] = avge*(8 - i) / (7 - i) - left;
	           left = a[i] + left -avge;
	        }
	    }
		for(int j=0;j<6;j++){//遍历输出每个儿子原来的桔子个数
			System.out.print(a[j]+" ");
		}
	}
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值