蓝桥志11——找新娘

问题

假设一共有N对新婚夫妇,其中有M个新郎找错了新娘,求发生这种情况一共有多少种可能.
Input

输入数据的第一行是一个整数C,表示测试实例的个数,然后是C行数据,每行包含两个整数N和M(1<M<=N<=20)。

Output

对于每个测试实例,请输出一共有多少种发生这种情况的可能,每个实例的输出占一行。

Sample Input

2
2 2
3 2

Sample Output

1
3

分析:

这个题有两层含义,n对夫妇中选m对,m对夫妇进行错排
错排:每队夫妇都和伴侣匹配,错排的目的是使每组都匹配到错的人。
f(n,m) = Cn_m * 错排(m)
错排:f(n)
f(n) = (n-1) * f(n-1):前面n-1个人已经形成了错排,有f(n-1)中可能,那么第n个人只要和前n-1中任一交换即可
f(n) = (n-1) * f(n-2):前面n-1个人没有错排好,要和第n个人一起形成整体错排则前n-1个人里只允许一对夫妇是正确的,
则问题变成从n-1个人选一个(n-1中情况)用来和第n个人交换,剩下n-2个人错排,即(n-1)*f(n-2)
f(n) = (n-1) * (f(n-1) + f(n-2))
最后想了一下出发点,应该是讨论f(n-1)已知和f(n-1)未知,从这里切入。

代码

import java.util.Scanner;
public class Main {
	private static long[][] C_n_m = new long[21][21];
	private static long[] A_m = new long[21];
	private static void init() {
		A_m[0] = 0;
		A_m[1] = 0;
		A_m[2] = 1;
		for(int i = 0 ; i <= 20 ; i++) {//组合
			for(int j = 0; j <= i ; j++) {
				if(j==0)
					C_n_m[i][j] = 1;
				else
					C_n_m[i][j] = C_n_m[i-1][j-1] + C_n_m[i-1][j];
			}
		}
		
		for(int i = 3 ; i <= 20 ; i ++) {//错排
			A_m[i] = (A_m[i-1]+A_m[i-2])*(i-1);
		}
	}
	public static void main(String[] args) {
		init();
		Scanner sc = new Scanner(System.in);
		int num = sc.nextInt();
		int[][] in = new int[num][2];
		for(int i = 0; i < num ; i++) {
			in[i][0] = sc.nextInt();
			in[i][1] = sc.nextInt();
		}
		
		for(int i = 0; i < num ; i++) {
			System.out.println(C_n_m[in[i][0]][in[i][1]]*A_m[in[i][1]]);
		}
		sc.close();
	}
}

当然用Cnm的数学公式,阶乘得到组合数也行,但明显没有递推简单

import java.util.Scanner;
class Main{
  public static void main(String[] args) {		
				Scanner sc = new Scanner(System.in);
				int n = sc.nextInt();
				int[][] in = new int[n][2];
				long[] res = new long[20];
				long[] cuopai = new long[21];
				cuopai[0] = 0;
				cuopai[1] = 0;
				cuopai[2] = 1;
				for(int i = 3 ; i <= 20 ;i ++) {
					cuopai[i] = (i - 1) * (cuopai[i-1] + cuopai[i -2]);
					}
				//输入并保存数据
				for(int i = 0; i < n ; i++) {
					in[i][0] = sc.nextInt();
					in[i][1] = sc.nextInt();
				}
				for(int i = 0; i < n ; i++) {
					if(in[i][0] == in[i][1] || in[i][0] < in[i][1])
						res[i] = 1* cuopai[in[i][1]];
					else {
						long fenmu = jiecheng(in[i][1]);
						long fenzi = jiecheng(in[i][0])/jiecheng(in[i][0] - in[i][1]);
						long C_n_m = fenzi / fenmu;
						res[i] = C_n_m * cuopai[in[i][1]];
					}
				}
				for(int i = 0; i < n ; i++) {
					System.out.println(res[i]);
				}
				
			}
				
			private static long jiecheng(int x) {
				if(x==0 || x==1)
					return 1;
				long res = 1;
				for(int i = x ; i > 0 ; i--) {
					res *= i;
				}
				return res;
			}
}

同时记录下提交的各个版本做分析总结

  • version1.0:
    只看到了第一层,做了个组合公式的java描述
import java.util.Scanner;
public class Main{
	public static void main(String[] args) {
		Scanner sc = new Scanner(System.in);
		int n = sc.nextInt();
		int[][] in = new int[n][2];
		long[] res = new long[20];
		
		for(int i = 0; i < n ; i++) {
			in[i][0] = sc.nextInt();
			in[i][1] = sc.nextInt();
		}
		for(int i = 0; i < n ; i++) {
			if(in[i][0] == in[i][1])
				res[i] = 1;
			else {
				long fenmu = jiecheng(in[i][0]);
				long fenzi = jiecheng(in[i][1])/jiecheng(in[i][1] - in[i][0]);
				res[i] = fenzi / fenmu;
			}
		}
		for(int i = 0; i < n ; i++) {
			System.out.println(res[i]);
		}
		
	}
	private static long jiecheng(int x) {
		long res = 1;
		for(int i = x ; i > 0 ; i--) {
			res *= i;
		}
		return res;
	} 
}
  • version2.0:
    原来还有第二层,那该吧,此时还没有注意到边界,结果提交后还是wa,答案错误,以为是算法问题,那我改成递推试试,得到version3
public static void main(String[] args) {		
				Scanner sc = new Scanner(System.in);
				int n = sc.nextInt();
				int[][] in = new int[n][2];
				long[] res = new long[20];
				long[] cuopai = new long[20];
				cuopai[0] = 0;
				cuopai[1] = 0;
				cuopai[2] = 1;
				for(int i = 3 ; i < 20 ;i ++) {
					cuopai[i] = (i - 1) * (cuopai[i-1] + cuopai[i -2]);
					}
				//输入并保存数据
				for(int i = 0; i < n ; i++) {
					in[i][0] = sc.nextInt();
					in[i][1] = sc.nextInt();
				}
				for(int i = 0; i < n ; i++) {
					if(in[i][0] == in[i][1] || in[i][0] < in[i][1])
						res[i] = 1;
					else {
						long fenmu = jiecheng(in[i][1]);
						long fenzi = jiecheng(in[i][0])/jiecheng(in[i][0] - in[i][1]);
						long C_n_m = fenzi / fenmu;
						res[i] = C_n_m * cuopai[in[i][1]];
					}
				}
				for(int i = 0; i < n ; i++) {
					System.out.println(res[i]);
				}
				
			}
				
			private static long jiecheng(int x) {
				if(x==0 || x==1)
					return 1;
				long res = 1;
				for(int i = x ; i > 0 ; i--) {
					res *= i;
				}
				return res;
			}
	}
  • version3.0
    此时还是没有注意到边界问题,提交后还是wa!但递推用在处理此题上明显比我的version2.0要更加直接更加高效更加优雅。
public static void main(String[] args) {
		init();
		Scanner sc = new Scanner(System.in);
		int num = sc.nextInt();
		int[][] in = new int[num][2];
		for(int i = 0; i < num ; i++) {
			in[i][0] = sc.nextInt();
			in[i][1] = sc.nextInt();
		}
		
		for(int i = 0; i < num ; i++) {
			System.out.println(C_n_m[in[i][0]][in[i][1]]*A_m[in[i][1]]);
		}
		sc.close();
	}
	private static long[][] C_n_m = new long[20][20];!!!注意边界1<m<=n<=20
	private static long[] A_m = new long[20];!!!注意边界
	private static void init() {
		A_m[0] = 0;
		A_m[1] = 0;
		A_m[2] = 1;
		for(int i = 0 ; i < 20 ; i++) {//组合
			for(int j = 0; j <= i ; j++) {
				if(j==0)
					C_n_m[i][j] = 1;
				else
					C_n_m[i][j] = C_n_m[i-1][j-1] + C_n_m[i-1][j];
			}
		}
		
		for(int i = 3 ; i < 20 ; i ++) {//错排
			A_m[i] = (A_m[i-1]+A_m[i-2])*(i-1);
		}
	}

经验

  • 注意边界!注意边界!注意边界
  • 递推是真好用,递推问题关键是抽象出公式来,结合我做的几道其他题来看,一般将问题分成两个部分,如前n-1个和最后一个,要么讨论前n-1个,要么从第n个反推,即讨论n的情况反过来推前n-1项。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值