问题描述
限制时间: 0.5秒
算法问题: 我想将西部的站点连接到东部的站点。 (此时,只有一座桥可以连接到一个站点。)因为我尝试建造尽可能多的桥梁,所以我尝试建造尽可能多的(N)桥以西侧的站点数量。桥梁不能相互重叠。 此时,编写一个程序来计算您可以建立多少个案例。
输入: 输入的第一行给出了测试用例的数量,'T'。 从下一行开始,每个测试用例都被赋予一个整数 (0
输出: 对于每个测试用例,打印在给定条件下可以构建桥梁的用例数。
示例:
Input Output
3
2 2 1
1 5 5
13 29 67863915
这是我的代码:
#include <stdio.h>
int combination(int n,int r) {
if (n == r || r == 0) return 1;
else return combination(n - 1,r - 1) + combination(n - 1,r);
}
int main(void)
{
int Tcase;
int N,M;
scanf("%d",&Tcase);
for (int i = 0; i < Tcase; i++) {
int total;
scanf("%d %d",&N,&M);
if (M - N == 0)
total = 1;
else
total = combination(M,N);
printf("%d\n",total);
}
return 0;
}
解决方法
函数调用会增加一些开销。冗余函数调用会增加很多开销。因为所有基本情况函数调用都返回 1,所以您可以计算将导致基本情况的函数调用次数。
您可以将整个递归调用堆栈展平为一个整数数组,您可以在其中计算某个状态发生的次数。此处 mem[i]
表示在您的程序版本中将调用 combination(n,i)
的次数。 (请注意,此语句仅在 while 循环的每次迭代结束时才严格正确)
int combination(int n,int r) {
int* mem = malloc(sizeof(int)*(r+1));
// the largest index in mem is r
if (mem == NULL) return -1;
for (int i = 0; i < r; ++i) {
mem[i] = 0;
}
mem[r] = 1;
// here we have mem = 0,...,1
int total = 0;
// total is the number of function
// calls in the original program that
// will result in the base case
while (n > 0) {
// if (r == 0) return 1;
total += mem[0];
mem[0] = 0;
// if (n == r) return 1;
if (n <= r) {
total += mem[n];
mem[n] = 0;
}
// else return combination(n - 1,r - 1) + combination(n - 1,r);
for (int i = 0; i < r; ++i) {
mem[i] += mem[i+1];
}
--n;
}
free(mem);
return total;
}
即使这样也不是最理想的;可能不需要对 mem[0]
的内存访问,并且绝对可以减少第二个 for
循环的界限。