问题描述
每年冬天,北大未名湖上都是滑冰的好地方。北大体育组准备了许多冰鞋,可是人太多了,每天下午收工后,常常一双冰鞋都不剩。
每天早上,租鞋窗口都会排起长龙,假设有还鞋的m个,有需要租鞋的n个。现在的问题是,这些人有多少种排法,可以避免出现体育组没有冰鞋可租的尴尬场面。(两个同样需求的人(比如都是租鞋或都是还鞋)交换位置是同一种排法)输入格式
两个整数,表示m和n
输出格式
一个整数,表示队伍的排法的方案数。
样例输入
3 2
样例输出
5
数据规模和约定
m,n∈[0,18]
问题分析
考点:递归 递推
问题分析: 这是一种类似汉诺塔问题的题目首。先要保证换鞋的人m多于借鞋的人n。否则,无论怎么排序,都是会出现无鞋可借的尴尬局面。
所以,首先判断(m>n),否则返回0,表示没有方法来排序。
之后,当还鞋的人数n为0 的时候,这时候无论怎么排序都是一种方法,所以这时候函数返回的是1;
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
using namespace std;
int func(int n,int m){
if(n<m) return 0;
if(m==0) return 1;
return func(n-1,m)+func(n,m-1);
}
int main(){
int n,m;
scanf("%d%d",&n,&m);
printf("%d\n",func(n,m));
return 0;
}
PS:这里的return fun(m-1,n)+fun(m,n-1)——前面的fun(m-1,n)意思是还鞋子的一个人站在最前面,之后剩下的人再接着排序,fun(m,n-1) 意思是借鞋子的人站在最后面,剩下的再接着排序。
OR——DP法
dp[i][j] 表示当前有i个人还鞋,j个人借鞋时有多少种排法。需要注意的是,最开始先放还鞋的,并且在排的队列中当前位置之前借鞋的人数不能超过还鞋的人数,不然就借不到鞋了。
当i=j时,dp[i][j]=dp[i][j-1]
当i>j时,当前位置随便放借鞋还是还鞋都可以,
dp[i][j]=dp[i-1][j-1]+dp[i][j-1]
#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
using namespace std;
int dp[20][20];
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++){
dp[i][0]=1;
for(int j=1;j<=m;j++){
if(i>j)
dp[i][j]=dp[i][j-1]+dp[i-1][j];
else if(i==j)
dp[i][j]=dp[i][j-1];
}
}
printf("%d\n",dp[n][m]);
return 0;
}
或者用一种接近递归思想的非递归算法:
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
int dp[20][20];
int main() {
int m,n;
memset(dp,0,sizeof(dp));
for(int i=1; i<=18; i++) {
dp[i][0] = 1;
for(int j=1; j<=i; j++)
dp[i][j] = dp[i-1][j] + dp[i][j-1];
}
scanf("%d%d",&m,&n);
printf("%d\n",dp[m][n]);
return 0;
}