问题描述
每年冬天,北大未名湖上都是滑冰的好地方。北大体育组准备了许多冰鞋,可是人太多了,每天下午收工后,常常一双冰鞋都不剩。
每天早上,租鞋窗口都会排起长龙,假设有还鞋的m个,有需要租鞋的n个。现在的问题是,这些人有多少种排法,可以避免出现体育组没有冰鞋可租的尴尬场面。(两个同样需求的人(比如都是租鞋或都是还鞋)交换位置是同一种排法)
输入格式
两个整数,表示m和n
输出格式
一个整数,表示队伍的排法的方案数
分析
最开始我想复杂了,想用全排列去做,还鞋子是1,借是-1,这样的话就是生成所有的排列,然后算前缀和,小于0就break,但是m,n的范围比较大了,这么做就不太可行,很傻,有一个点过不去。后来寻思打个表,哈哈哈。
import copy
m,n=input().split()
m=int(m)
n=int(n)
num=0
def f(p,_str,n):
global num
if(_str[0]==-1):
return
if p==n:
if(_str[0]==1):
lpx=copy.deepcopy(_str)
for i in range(1,len(_str)):
lpx[i]=lpx[i]+lpx[i-1]
if(lpx[i]<0):
break
if i==len(_str)-1:
num+=1
return
_str_part=sorted(_str[p:])
_str[p:]=_str_part
for i in range(p,n,1):
if(i>p and _str[i]==_str[i-1]):
continue
_str[i],_str[p]=_str[p],_str[i]
f(p+1,_str,n)
_str[i],_str[p]=_str[p],_str[i]
aa=[1 for i in range(m)]
bb=[-1 for i in range(n)]
cc=aa+bb
f(0,cc,m+n)
print(num)
分析:
基于上面用1和-1表示的方法,还鞋用A表示,借鞋用B表示,可以抽象成m个A和n个B排列,要求是任意前i个必有A的个数不少于B,所以第一个一定是A,然后下一个数可能是A或B,如果是A的话,跟第一个数的情况相同,如果是B的话下一个只能选A(摘抄的别人的思路)
所以有了大家搜到的那个f(x,y)=f(x-1,y)+f(x,y-1)
用自然语言解释下就是f(x,y)表示剩下x个A,y个B(因为我们要排列A和B嘛),如果截止目前,还鞋子的比借鞋子的多,那么无所谓,下一个要么取A,要么取B。代码实现如下:
import copy
m,n=input().split()
m=int(m)
n=int(n)
def f(x,y):
if(x==0 or y==0):
return 1
if(m-x==n-y):
return f(x-1,y)
if(m-x>n-y):
return f(x-1,y)+f(x,y-1)
if m<n:
print(0)
else:
print(f(m,n))