运行要求
运行时间限制: 2sec
内存限制: 1024MB
原文连接算法
题目
二次元的坐标系的原点坐标(0,0)上有一名骑士,骑士若是处在格子(i,j)上的话能够按照以下的方式移动
(i+1,j+2)
(i+2,j+1)
若是骑士想到达(X,Y)的话,有多少种移动方案。数组
输入前提条件微信
输入
输入都以如下标准从命令行输入app
X Y
输出
求骑士从原点(0,0)到(X,Y)的移动方法的总数,结果取10^9+7的余数spa
例1
输入命令行
3 3
输出code
2
(0,0)->(1,2)->(3,3)
(0,0)->(2,1)->(3,3)
两种方法blog
例2
输入内存
2 2
输出get
0
骑士不能移动到(2,2)的坐标
例3
输入
999999 999999
输出
151840682
最后的结果是和10^9+7相除的余数
读懂题目
这题能够抽象成坐标系的题目
解题思路
1.首先有下面两种走法,红色的长方形和蓝色的长方形
咱们设红色的长方形的数量是m,蓝色的长方形的数量是n。那么m和n知足如下条件
2*m + n == X 2*n + m == Y
咱们从0开始遍历m,能够找到全部知足条件的m和n的组合
遍历的条件是如下
2*m <= X m <= Y
另外X+Y = 3m + 3n = 3*(M+N)
说明X+Y必须为3的倍数,否则的话经过红色或者蓝色长方形是到达不了(X,Y)的
就这样咱们找到了可能的m,n,题目要求的是全部移动方法的数量。这里咱们能够抽象成如下的问题
2.有m个大小同样的红球,n个同样的蓝球。咱们任意排列这些球,请问有多少种排法?
这一高中数学排列组合的一道经典题目
答案是C(m+n,n)
证实大概是以下
咱们把全部的排列组合计算一下是(m+n)的阶乘
这些组合里面,由于蓝色的球都是同样的球和红色的球的也都是同样的球。
因此
全部的排列组合(m+n)的阶乘 = 红色的球的排列组合(m的阶乘)某个数α
全部的排列组合(m+n)的阶乘 = 蓝色的球的排列组合(n的阶乘)乘以某个数β
剩下的(m+n)的阶乘/(m的阶乘*n的阶乘)就是去掉红球和蓝球排列的组合
C(m+n,m) = (m + n)! / (m+n-m)! * m!
3.接下来如何求得答案,这也是很使人头疼的问题
由于目标点坐标X,Y的数值最大可能达到10^6,相应的m,n的值也可能回答道10^5这么大,咱们贸然的对如此大的数去作阶乘显然会overflow
那么咱们怎么办呢,看看结果公式
(m + n)! / (m+n-m)! * m!
(m+n)的阶乘的时候咱们能够每乘法一次,对10^9+7取余数,这样把数值控制在一个很小的范围之内
可是 /(m+n-m)! * m!这些怎么办
咱们根据费马小定律有以下推断,如图
x^mod-2和1/x相互为对mod取余的逆元
求1/x对mod的余数就至关于求x^mod-2对mod的余数
结果能够当作((m + n)! (1/1 1/2 1/3 .... 1/(n)) (1/1 1/2 1/3 .... 1/(m)) } % mod
咱们能够依次求的这些
1/1 % mod
1/2 % mod
1/3 % mod
1/x % mod
可是这样带来一个问题,mod=10^9+7很是大的一个数n^(mod-2)显然也很差计算
有另一种算法
(1/x) % mod = - ((1/(mod % x) % mod) * (mod / x)
咱们设定一个数组叫inverse
inverse[x] = (1/x) % mod inverse[x] = (-1* inverse[mod % x] * (mod //2 i)) % mod x=0的时候inverse[0] = 0 x=1的时候inverse[1] = 1 x=2的时候inverse[2] = (-1 * inverse[1] * (mod //2)) % mod x=3的时候inverse[3] = (-1 * inverse[2] * (mod //3)) % mod ...
代码
X, Y = map(int,input().split()) def cmb(n, k, mod, fac, ifac): k = min(k, n-k) return fac[n] * ifac[k] * ifac[n-k] % mod def make_tables(mod, n): fac = [1, 1] # 储存阶乘的数组 ifac = [1, 1] # 储存逆元阶乘的数组 inverse = [0, 1] # 储存逆元的数组 for i in range(2, n+1): fac.append((fac[-1] * i) % mod) inverse.append((-inverse[mod % i] * (mod//i)) % mod) ifac.append((ifac[-1] * inverse[-1]) % mod) return fac, ifac def comb(m,n): MOD = 10**9 + 7 fac, ifac = make_tables(MOD, m) ans = cmb(m, n, MOD, fac, ifac) return ans def calculate(x,y): m = 0 n = 0 if ((x + y) % 3) != 0: print(0) return result = [] while (2*m <= x) and (m <= y): n = x - 2 * m if (2*m + n == x) and (2*n + m == y): result.append(comb(m+n,n)) m = m + 1 if len(result) == 0: print(0) else: print(min(result)) calculate(X,Y)
总结
这道题有3个难点
最后本篇文章所提交的代码里关于求排列组合的代码,是网上寻找的。有心的话,能够收藏,目前代码竞技的职业玩家在排列组合上应该也是使用的这些代码
※ 另外,我会在个人微信我的订阅号上推出一些文章,欢迎关注