【组合数学】购票问题
购票问题
题目大意:一张票50元,有N个带着50元的人和N个带着100元的人,请问总共有多少种排队方法使得不会出现购票找不回钱的尴尬局面?
输入样例:2
输出样例:2
这是一类非常有代表性的问题,下面将介绍该问题的5种解法
Number 5:暴力枚举
很显然,要使带着100元的购票那么就需要50的去找给他。
那么可以抽象的看做当50元购票时50元的票数+1
而当100元购票时50元的票数-1
又因为100元的并不能找钱用,所以可以不做考虑……
那么很容易可以看出我们在收取一个人50元的时候50元的个数可以+1
所以定义一个计数变量k
那么我们可以决定此时是选择50的人来买票或者是100的人来买票。
因为50找100,他们都有N个,则互相抵消。
最后可以直接看k是否==0
代码如下:
#include<iostream> #include<cstring> #include<cstdio> using namespace std; int N; int k; int ans; void dfs(int tmp){//回溯 if(tmp==2*N){//如果全排完了 if(!k) ans++;//满足出现了N个100 N个50答案数+1 return ; } if(k){//如果还有50的 k--;//就可以让100的来 dfs(tmp+1);//深入 k++;//恢复 } k++;//在加一个50的显然不成问题 dfs(tmp+1);//搜索 k--; } int main(){ cin>>N; dfs(0); cout<<ans; }
Number 4:栈模型
此类算法就是生成一串排列并在中途判断
时间复杂度>Number 5
空间复杂度>Number 5
代码复杂度>Number 5
思考复杂度>Number 5
编码复杂度>Number 5
你说我还讲它干嘛?
Number 3:DFS(递归)
我们可以把问题分为3个阶段:
其中a代表50张数,b代表100张数
①不满足条件类型
1.当a<b时
2.当a>N时
3.当b>N时
②当a==b==N时
全都符合条件,答案数+1
③直接继续搜索
代码如下:
#include<iostream> using namespace std; int cnt,n; void dfs(int a,int b) { if(a<b||a>n||b>n) return ; else if(a==n&&b==n) cnt++; else{dfs(a,b+1);dfs(a+1,b);} } int main() { cin>>n; dfs(0,0); cout<<cnt; }
Number 2:动态规划or记忆化搜索
参见Number 3,思想基本上相同,较好理解
#include<iostream> using namespace std; inline int read() { int x,f=1; char ch=getchar(); for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1; for(x=ch-'0';isdigit(ch=getchar());x=x*10+ch-'0'); return x*f; } inline void write(int x){ if(x==0){putchar('0');return;}if(x<0)putchar('-'),x=-x; int len=0,buf[15];while(x)buf[len++]=x%10,x/=10; for(int i=len-1;i>=0;i--)putchar(buf[i]+'0');return; } long long dp[100][100]; int a,b,n; int main() { n=read(); dp[0][0]=1; for(a=1;a<=n;a++) for(b=0;b<=a&&b<=n;b++) { if((b==n||b==a)&&a==n)dp[a][b]=dp[a-1][b]+dp[a][b-1]+1; else dp[a][b]=dp[a-1][b]+dp[a][b-1]; } write(dp[n][n]-1); }
Number 1:卡特兰数
答案数=C(2n,n)/(n+1)
答案数可以看做为总排列数-不符合要求数
而可以看出总排列数为2N里面选N个,那么就是C(2N,N)
可以抽象的看做当50元购票时50元的票数+1
而当100元购票时50元的票数-1
也就是50的个数要始终≥100的个数
不符合条件的就是在一段里面有m+1个100元
有m个50元
那么可以看做是从2N个里面选择N+1个
即C(2n,n+1)
C(2N,N)-C(2N,N+1)=C(2N,N)/(N+1)
代码如下:
#include<iostream> using namespace std; int main(){ int n; cin>>n; long long ans=1; for(int i=0;i<n;i++) ans=ans*(2*n-i)/(i+1); cout<<ans/(n+1); }