过年期间,老家举行了一场盛大的集体婚礼,为了使婚礼进行的丰富一些,司仪临时想出了有一个有意思的节目,叫做“考新郎”,具体的操作是这样的:
1. 首先,给每位新娘打扮得几乎一模一样,并盖上大大的红盖头随机坐成一排;
2. 然后,让各位新郎寻找自己的新娘。每人只准找一个,并且不允许多人找一个;
3. 最后,揭开盖头,如果找错了对象就要当众跪搓衣板...
假设一共有n对新婚夫妇,其中有m个新郎找错了新娘,求发生这种情况一共有多少种可能。
输入包含多组数据。每组数据包含两个正整数n和m(2≤m≤n≤20)
对应每一组数据,输出一个正整数,表示n对新人中有m个新郎找错新娘的种数。
2 2<br/>3 2
1<br/>3
#include <iostream> #include <stdio.h> #define N 21 using namespace std; int main() { long long f[N] = {1, 0, 1}; long long fac[N] = {1, 1, 2}; for(int i=3; i<N; i++) { f[i] = (i-1)*(f[i-1] + f[i-2]); fac[i] = i*fac[i-1]; } int n, m; while(~scanf("%d %d", &n, &m)) { printf("%lld\n", fac[n]/fac[m]/fac[n-m]*f[m]); } return 0; }
import sys
from math import factorial as f
#求错位数的函数
def dislocationNumber(n):
arr = [0, 1]
while len(arr) < n:
arr.append(len(arr) * (arr[-1] + arr[-2]))
return arr[-1]
for i in sys.stdin.readlines():
n, m = map(int, i.strip().split())
print(dislocationNumber(m) * f(n) // (f(m) * f(n - m)))
首先我们需要从n对夫妻中选出m对夫妻,总共有Cm,n=n!/m!/(n - m)!
种情况。
接着这m对夫妻的新郎全部选错的情况f(m) = (m - 1) * (f(m - 1) * f(m - 2))
,这个公式上前面好几道都推过,请参考我的博客 PAT乙级(Basic Level)练习题 发邮件
因此公式为:n!/m!/(n - m)! * f(m)
#include <iostream> using namespace std; int main() { int m = 0, n = 0; //fTable[n]记录n个人都悬错的情况种数,allTable[n]记录n的阶乘 long long fTable[21] = {0, 0, 1}, allTable[21] = {1, 1, 2}; for (int i = 3; i < 21; ++i) { //递推计算i个人全部拿错 fTable[i] = (i - 1) * (fTable[i - 1] + fTable[i - 2]); //递推计算i的阶乘 allTable[i] = i * allTable[i - 1]; } //scanf返回值为正确输入数据的变量个数,当一个变量都没有成功获取数据时,此时返回-1 while (scanf("%d %d", &n, &m) != - 1) { //组合公式Cm,n=n!/m!/(n - m)!,从n个中随机s选m个 //从n对中选出m对,allTable[n] / allTable[m] /allTable[n - m]情况 //选出的m对夫妻全部选错,fTable[m]种情况 printf("%lld\n", allTable[n] / allTable[m] /allTable[n - m] * fTable[m]); } return 0; } ———————————————— 版权声明:本文为CSDN博主「hestyle」的原创文章,遵循 CC 4.0 BY 版权协议,转载请附上原文出处链接及本声明。 原文链接:https://hestyle.blog.csdn.net/article/details/104690822
import java.math.BigInteger; import java.util.*; public class Main{ public static void main(String[] args){ Scanner sc=new Scanner(System.in); BigInteger[] der=new BigInteger[21]; der[0]=new BigInteger("0"); der[1]=new BigInteger("0"); der[2]=new BigInteger("1"); for(int i=3;i<21;i++){ der[i]=(der[i-2].add(der[i-1])).multiply(new BigInteger(i-1+"")); } while(sc.hasNext()){ int n=sc.nextInt(); int m=sc.nextInt(); System.out.println(der[m].multiply(jiechen(n)).divide(jiechen(m).multiply(jiechen(n-m)))); } } public static BigInteger jiechen(int n){ BigInteger result=new BigInteger("1"); for(int i=2;i<=n;i++){ result=result.multiply(new BigInteger(i+"")); } return result; } }
f[i][j]表示有i对新婚夫妇有j个新郎找错新娘的种数
容易知道当前有i - 1对新婚夫妇时加一对新婚夫妇进来时无非有三种情况:
1)加进来的新郎找对了,该有多少人找错还是有多少人找错 --> f[i][j] += f[i - 1][j]
2)加进来的新郎与前i - 1个中找对的新郎互换,找错对数加2 -->f[i][j] += f[i - 1][j - 2] * (i - 1 - (j - 2))
3)加进来的新郎与前i - 1个中找错的新郎互换-,找错对数加1 -->f[i][j] += f[i - 1][j - 1] * (j - 1)
//n个中取m个,对m个错排 #include <stdio.h> #include <stdlib.h> long long int find[21] = {0, 0, 1, 2}; long long int func[20] = {1, 1, 2, 6}; //n!/(m!(n-m)!) long long int fun(int); int main() { int i; for(i = 3; i < 21; i++) { find[i] = (i-1) * (find[i-1] + find[i-2]); func[i] = fun(i); } int m, n; while(~scanf("%d %d", &n, &m)) { long long int result = find[m] * (func[n] / (func[m] * func[n-m])); printf("%lld\n", result); } } long long int fun(int i) { if(i == 0 || i == 1) return 1; return i * fun(i-1)
def cal(a,b): con1,con2 = 1,1 for i in range(1,b+1): con1 = con1*i for i in range(a,a-b,-1): con2 = con2*i return con2//con1*f[b] f = [0,0,1]+[0 for i in range(3,21)] for i in range(3,21): f[i] = (i-1)*(f[i-1]+f[i-2]) try: while True: n,m = map(int,input().split()) print(cal(n,m)) except: pass
思路 : 本质上是信封问题。和排列组合问题的组合 #include <vector> #include <iostream> using namespace std; vector<long long> v(22); long long Fac(int n) { register long long f = 1; while (n != 1 && n > 0) { f *= n; n--; } return f; } long long C_n_m(int n, int m) { return Fac(n) / (Fac(n - m) * Fac(m)); } int main() { v[0] = 0; v[1] = 0; v[2] = 1; for (int i = 3; i < 21; i++) { v[i] = (i - 1)*(v[i - 1] + v[i - 2]); } int n, m; while (cin >> n >> m) { long long temp = n - m; temp = C_n_m(n, m)*v[m]; cout << temp << endl; } return 0; }
import java.util.Scanner; public class Main { public static long fn(int m){ if(m == 1){ return 0; }else if(m == 2){ return 1; }else{ return (m-1)*(fn(m-1)+fn(m-2)); } } public static long JieCheng(int n){ long num = 1; for(int i=n;i>=1;i--){ num = num * i; } return num; } public static long C(int n,int m){ long part1 = JieCheng(n); long part2 = JieCheng(m); long part3 = JieCheng(n - m); return part1 / (part2 * part3); } public static void main(String[] args){ Scanner sc = new Scanner(System.in); while(sc.hasNext()){ int n = sc.nextInt(); int m = sc.nextInt(); long result = C(n,m)*fn(m); System.out.println(result); } } }
只是变式的错排问题而已,无非多了一个组合数~ #include<stdio.h> long long jiecheng(int n) { long long ans=1; int i; for(i=1;i<=n;i++) ans*=i; return ans; } long long C(int n,int m) { return jiecheng(n)/(jiecheng(n-m)*jiecheng(m)); } int main() { long long f[22]; int i,m,n; f[1]=0;f[2]=1; for(i=3;i<=21;i++) { f[i]=(i-1)*(f[i-1]+f[i-2]); } while(~scanf("%d%d",&n,&m)) { printf("%lld\n",C(n,m)*f[m]); } return 0; }
#include <stdio.h> long long all(int n){ if(n==2) return 1; if(n==3) return 2; else{ return (all(n-1)+all(n-2))*(n-1); } } int main() { int m,n,i; while(scanf("%d %d",&n,&m)!=EOF){ long long ms=1; long long ns=1; long long mns=1; long long s=all(m); if(m==n){ printf("%lld\n",s); }else{ for(i=1;i<=n;i++){ ns*=i; } for(i=1;i<=m;i++){ ms*=i; } for(i=1;i<=(n-m);i++){ mns*=i; } printf("%lld\n",ns/ms/mns*s); } } return 0; }