瞬间移动
Accepts: 1018 Submissions: 3620
Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)
Problem Description
有一个无限大的矩形,初始时你在左上角(即第一行第一列),每次你都可以选择一个右下方格子,并瞬移过去(如从下图中的红色格子能直接瞬移到蓝色格子),求到第n行第m列的格子有几种方案,答案对1000000007取模。
Input
多组测试数据。
两个整数n,m(2≤n,m≤100000)
Output
一个整数表示答案
Sample Input
4 5
Sample Output
10
我的代码
一、递归法
思路:到达第n行第m列的格子的方案数=到达第i行(1-n)第j(1-m)列的格子的方案数的总和+1(直接从1,1移动到目标处)。用递归实现。
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <iostream>using namespace std;long fun(int a, int b) {long result = 1;if (a == 2 || b == 2) {return result;}ielse {for (int i = a - 1;i > 1;i--) {for (int j = b - 1;j > 1;j--) {result += fun(i, j);result = result % 1000000007;}}}return result;
}
int main() {int n, m;while (cin >> n >> m) {cout << fun(n, m) << endl;}return 0;
}
缺点:超时。时间复杂度高。对靠近1,1处的许多格子的路径数进行了大量重复计算。可不可以减少这些冗余计算?
二、用二维数组存储
思路:要计算到达第n行第m列的格子的方案数,就要分别计算到达第i(1-n)行第j(1-m)列的格子的方案数,然后对他们求和。为了避免重复计算,可以将每一个格子对应的到达它本身的方案数存起来。这样可以大大减小冗余计算,减小时间复杂度,代价是空间消耗。
#include <iostream>using namespace std;long a[100000][100000];long result;
void sum(int b, int c) {for (int i = 0;i<b;i++) {for (int j = 0;j<c;j++) {a[b][c] += a[i][j];a[b][c] = a[b][c] % 1000000007;}}return;
}
int main() {int n, m;while (cin >> n >> m) {result = 1;for (int i = 2;i < n;i++) {for (int j = 2;j < m;j++) {if (i == 2 || j == 2)a[i - 2][j - 2] = 1;else {sum(i - 2, j - 2);}}}cout << a[n - 2][m - 2] << endl;}return 0;
}
缺点:空间复杂度太大,n和m最大是100000,所以这种方法所需要的数组长度最大竟然可达10的10次方,即80GB!
改进思路:在方法二的基础上,先考虑正方形这一特殊情况,用大小为100000的数组存储到达第n行第n列的格子的方案数。不失一般性,假设m>n,则第n行第m列的格子的方案数比第n行第n列的格子多出了“从第(m-n)列到达目的”的方案数。这第(m-n)列可能是多列,行序号是2-(n-1)。不过这些处于正方形之外的格子和那些不在右斜对角线上的格子一样,又涉及重复计算,导致时间复杂度升高。所以这算是方法一和方法二的一种平衡。但时间复杂度能降多少,还有待实验验证。