第三节:典型问题的递归框架

第一题:蚂蚁感冒

有一根27厘米的细木杆,在第3厘米、7厘米、11厘米、17厘米、23厘米这五个位置上各有一只蚂蚁。
木杆很细,只能同时通过一只蚂蚁。
开始时,蚂蚁的头朝左还是朝右是任意的,它们只会朝前走或调头,但不会后退。
当任意两只蚂蚁碰头时,两只蚂蚁会同时调头朝反方向走。假设蚂蚁们每秒钟可以走1厘米的距离。

编写程序,求所有蚂蚁都离开木杆的最小时间和最大时间。

思路:因为蚂蚁之间没有区别,可以将其碰撞后转换方向这一操作当作两只蚂蚁互相穿过,那么所需的最大时间就是离端点最远的蚂蚁下去的时间。

代码就不写啦。

第二题:蚂蚁感冒

长100厘米的细长直杆子上有n只蚂蚁。它们的头有的朝左,有的朝右。 
每只蚂蚁都只能沿着杆子向前爬,速度是1厘米/秒。
当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。

这些蚂蚁中,有1只蚂蚁感冒了。并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。
请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。

【数据格式】
    第一行输入一个整数n (1 < n < 50), 表示蚂蚁的总数。
    接着的一行是n个用空格分开的整数 Xi (-100 < Xi < 100), Xi的绝对值,表示蚂蚁离开杆子左边端点的距离。正值表示头朝右,负值表示头朝左,数据中不会出现0值,也不会出现两只蚂蚁占用同一位置。其中,第一个数据代表的蚂蚁感冒了。

    要求输出1个整数,表示最后感冒蚂蚁的数目。

例如,输入:
3
5 -2 8
程序应输出:
1

再例如,输入:
5
-10 8 -20 12 25
程序应输出:
3

思路:迎着4号的5,6会被传染,进而迎着5,6的1,3 也会被传染,

两只蚂蚁相撞同时掉头可以看作穿过,第一只蚂蚁不管方向朝哪它右边的蚂蚁只要向左走就可能碰撞感染(特殊情况除外),同理,第一只蚂蚁左边的蚂蚁只要朝右边走也可能被感染,这样就很容易得到这样的公式

sum = 左边蚂蚁向右走的数量+右边蚂蚁向左走的数量+第一只蚂蚁本身=left+right+1;

即:左同向+右逆向+1

特殊情况是,当当第一只蚂蚁向左走的时候如果第一只蚂蚁左边没有向右爬行的蚂蚁,那么不管第一只蚂蚁右边有多少向左爬行,因爬行速度相同则右边的蚂蚁永远不可能被感染

同理,当第一只蚂蚁向右走的时候,如果第一只蚂蚁右边没有向左爬行的蚂蚁,那么同样第一只蚂蚁左边也永远不可能感染

未提交代码:

import java.math.*;
import java.util.*;

public class Main {
	
	public static void main(String[] args) {
		Scanner cin = new Scanner(System.in);
		while(cin.hasNext()){
			int n = cin.nextInt();
			int a[] = new int [55];
			int b[] = new int [55];
			int sum = 0,left = 0,right = 0;
			for(int i=1;i<=n;++i){
				a[i] = cin.nextInt();
				int flag = a[1];
				if(Math.abs(a[i])<Math.abs(flag)&&a[i]*flag>0) left++;
				if(Math.abs(a[i])>Math.abs(flag)&&a[i]*flag<0) right++;
			}
			sum = left+right+1;
			if(right == 0) System.out.println("1");
			else System.out.println(sum);
		}
	}
}

第三题:搭积木

 小明最近喜欢搭数字积木。一共有10块积木,每个积木上有一个数字,0~9。

搭积木规则:
每个积木放到其它两个积木的上面,并且一定比下面的两个积木数字小。
最后搭成4层的金字塔形,必须用完所有的积木。

下面是两种合格的搭法:

   0
  1 2
 3 4 5
6 7 8 9

   0
  3 1
 7 5 2
9 8 6 4    

请你计算这样的搭法一共有多少种?

思路:全排列中减去不符合条件的

import java.util.Arrays;

public class Main{
	static int cnt = 0;
    public static void main(String[] args)  {
    	int a[] = {0,1,2,3,4,5,6,7,8,9};
    	f(a,0);
    	System.out.println("cnt = "+cnt);
    }
    static void show(int a[]){
    	System.out.println("   "+a[0]);
    	System.out.println("  "+a[1]+" "+a[2]);
    	System.out.println(" "+a[3]+" "+a[4]+" "+a[5]);
    	System.out.println(a[6]+" "+a[7]+" "+a[8]+" "+a[9]);
    }
    static void check(int a[]){
    	if(a[1]<a[0]) return ;
    	if(a[2]<a[0]) return ;
    	if(a[3]<a[1]) return ;
    	if(a[4]<a[1]) return ;
    	if(a[4]<a[2]) return ;
    	if(a[5]<a[2]) return ;
    	if(a[6]<a[3]) return ;
    	if(a[7]<a[3]) return ;
    	if(a[7]<a[4]) return ;
    	if(a[8]<a[4]) return ;
    	if(a[8]<a[5]) return ;
    	if(a[9]<a[5]) return ;
    	
    	show(a);
    	cnt++;
    }
    static void f(int []a,int k){  //全排列
    	if(k==a.length-1){
    		check(a);
    		return ;
    	}
    	for(int i=k;i<a.length;++i){
    		{int t = a[k]; a[k] = a[i]; a[i] = t;}//试探
    		f(a,k+1);
    		{int t = a[k]; a[k] = a[i]; a[i] = t;}//回溯
    	}
    }
}

第四题:n个数中取m个 问有几种组合方式(组合数)

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

第五题:固定数目的组合问题

//固定数目的组合问题
//ABCDE中取3个
public class Main{
    public static void main(String[] args)  {
    	for(char i='A';i<='E';++i){
    		for(char j=(char)(i+1);j<='E';++j){
    			for(char k=(char)(j+1);k<='E';++k){
    				System.out.println(""+i+j+k);
    			}
    		}
    	}
    }
}

第六题:不固定数目的组合问题,L长的字串中取n个(再看!)

import java.util.List;
import java.util.Vector;

//某个串中取n个
public class Main{
    public static void main(String[] args)  {
    	List lst = f("ABCDE",3);
    	for(int i=0;i<lst.size();++i){
    		System.out.println(lst.get(i));
    	}
    }
    static List f(String s,int n){
    	List lst = new Vector();
    	
    	if(n==0){
    		lst.add("");
    		return lst;
    	}
    	for(int i=0;i<s.length();++i){
    		char x = s.charAt(i);
    		List t = f(s.substring(i+1),n-1);
    		for(int k=0;k<t.size();++k){
    			lst.add(""+x+t.get(k));
    		}
    	}
    	return lst;
    }
}

第七题:AABBBC中取三个,有哪些取法(再看!)

import java.util.List;
import java.util.Vector;

//某个串中取n个
public class Main{
    public static void main(String[] args)  {
    	int []data = {2,3,1};  //每个元素的最大个数
    	int []x = new int[data.length];  //每个元素取几个
    	
    	f(data,x,0,3);
    }
    //data[]:不动,限制条件
    //x[]:取法
    //k: 当前考虑位置
    //goal: 剩余名额
    static void f(int  []data,int []x,int k,int goal){
    	if(k==x.length){
    		if(goal ==0) 
    			show(x);
    		return ;
    	}
    	for(int i=0;i<=Math.min(goal, data[k]);++i){
    		x[k] = i;
    		f(data , x, k+1 ,goal-i);
    	}
    	x[k] = 0; //回溯
    }
    static void show(int []x){
    	for(int i=0;i<x.length;++i){
    		for(int k=0;k<x[i];++k){
    			System.out.print((char)('A'+i));
    		}
    	}
    	System.out.println();
    }
}




  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值