【题目描述】
把 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自带的编辑器中既不报错,但也不输出结果,还不知道原因,先记在这里。