運行要求
運行時間限制: 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個難點
最後本篇文章所提交的代碼裏關於求排列組合的代碼,是網上尋找的。有心的話,能夠收藏,目前代碼競技的職業玩家在排列組合上應該也是使用的這些代碼
※ 另外,我會在個人微信我的訂閱號上推出一些文章,歡迎關注