【题目描述】 把 1 ∼ 2020 放在 2 × 1010 的矩阵里。要求同一行中右边的比左边大,同一列中下边的比上边的大。一共有多少种方案? 答案很大,你只需要给出方案数除以 2020 的余数即可。
【解题思路】 网上不少人给出了这道题的C语言解题代码,核心的思路是用动态规划,这里有比较详细的解析,在这里直接copy一下代码:
#include <stdio.h> int DP[1011][1011]; int main() { int i, j; DP[1][0] = 1; for (i = 1; i <= 1010; i++) DP[i][0] = 1;//初始化 for (i = 1; i <= 1010; i++) { for (j = 1; j <= i; j++) { if (i == j) DP[i][j] = DP[i][j - 1]; else DP[i][j] = (DP[i - 1][j] + DP[i][j - 1]) % 2020; } } printf("%d", DP[1010][1010]); return 0; }题目用了1011×1011的二维数组来存储第1行填 i 个数,第2行填 j 个数时可能的情况数,从两层for循环可看出,其本质上是在调用递归。
用Python来解这道题也主要是用动态规划,但由于Python中没有数组这种数据结构,只能用一个dp_func()函数进行递归以代替上面代码中的dp数组的功能,dp[ i ] [ j ] 中的 i 和 j 便成了 dp_func( i, j ) 函数中的两个参数。 翻译过来,在Python中的代码如下:
def dp_func(i,j): if j>i: return 0 else: if j==0: if i==0: return 0 else: return 1 else: if j==i: return dp_func(i,j-1) else: return (dp_func(i-1,j)+dp_func(i,j-1))%2020 print(dp_func(1010,1010))但直接运行这个函数会报错,或是一直运行不出来执行结果,原因是Python当中对递归函数的调用次数存在限制,可使用下面的代码查看递归在本机上的运行上限:
import sys sys.getrecursionlimit()也可手动更改递归的运行限制次数:
import sys sys.setrecursionlimit(9000000) #这里设置大一些添加上这两句代码后,在Python自带的编辑器中运行程序不报错了,但还是运行不出结果,在网上找原因,有网友解释到,这是因为Python在递归时,每次用到之前的值,都要重新计算一次,如果将结果放到缓存中可提到运行速度,这便要用到 functool 模块中的 lru_cache 装饰器,可使用下面的代码来进行缓存设置:
from functools import lru_cache @lru_cache(maxsize=2020)maxsize的值表示设置的最大缓存次数,如果为none则表示没有次数限制。
更改后的完整代码如下:
import sys sys.setrecursionlimit(9000000) #这里设置大一些 from functools import lru_cache @lru_cache(maxsize=2020) #maxsize为缓存的最大次数,为2n时效果最佳,为none则表示不限制 def dp_func(i,j): if j>i: return 0 else: if j==0: if i==0: return 0 else: return 1 else: if j==i: return dp_func(i,j-1) else: return (dp_func(i-1,j)+dp_func(i,j-1))%2020 print(dp_func(1010,1010))这段代码很快在Spyder中执行出结果1340,但在Python自带的编辑器中既不报错,但也不输出结果,还不知道原因,先记在这里。