我正在尝试在codechef上解决问题,这是链接:
给定的问题陈述是:
Chef最近一直在研究斐波那契数列,并编写了代码打印出斐波那契数列的第k个项(1、2、3、5、8、13…)。他想知道他是否可以编写一个程序来为相似序列生成第k个项。更具体地说:
如果n <= k并且
- T(n,k)为1
- T(n,k)= T(n-1,k)+ T(n-2,k)+ T(n-3,k)…+ T(nk,k),如果n> k。 / li>
给出n和k,输出T(n,k)%(1000000007)作为答案可能非常大
输入:N和K这两个整数
输出:一个整数,为mod 1000000007系列的第n个项
约束:1≤N,K≤2 * 10 5
示例:
输入:7 5
输出:9
系列如下{1,1,1,1,1,5,9}
void fibo(int n, unsigned long k) {
unsigned long *a, c;
a = (unsigned long *)malloc(sizeof(unsigned long) * k);
for (unsigned long i = 0; i < k; i++) { //T(n,k)=1 when n<=k
*(a + i)=1;
}
for (unsigned long m = 0; m < n - 1; m++) {
c = *(a);
for (unsigned long j = 0; j < k - 1; j++) {
*(a + j) = *(a + j + 1);
c = c + *(a + j);
}
*(a + k - 1) = c;
}
printf("%d ", *(a) % 1000000007);
}
这适用于较小的值,但不适用于非常大的值。我得到了示例的结果,但是当我输入值200000 500
时,会得到错误的答案
答案 0 :(得分:1)
问题是您要计算模ULONG_MAX
的值,最后减少模1000000007
的结果。这不会给出正确的结果。您必须在每一步减少模数1000000007
以避免潜在的算术溢出(这不会引起类型unsigned long
的不确定行为,但得出的结果与预期结果不同)。
这是您代码的修改版本,具有更快的替代方法(在我的笔记本电脑上是两倍以上):
#include <stdio.h>
#include <stdlib.h>
#define DIVIDER 1000000007ul
unsigned long fibo(unsigned long n, unsigned long k) {
unsigned long c = 1;
if (n > k) {
unsigned long *a = (unsigned long *)malloc(sizeof(*a) * k);
for (unsigned long i = 0; i < k; i++) { //T(n,k)=1 when n<=k
a[i] = 1;
}
for (unsigned long m = k; m < n; m++) {
c = a[0];
for (unsigned long j = 0; j < k - 1; j++) {
a[j] = a[j + 1];
#if 0
// slower version using modulo
c = (c + a[j]) % DIVIDER;
#else
// faster version with a test
if ((c += a[j]) >= DIVIDER)
c -= DIVIDER;
#endif
}
a[k - 1] = c;
}
free(a);
}
return c;
}
int main(int argc, char *argv[]) {
if (argc <= 2) {
printf("usage: fibo n k");
return 1;
} else {
unsigned long n = strtoul(argv[1], NULL, 10);
unsigned long k = strtoul(argv[2], NULL, 10);
printf("%lu\n", fibo(n, k));
}
return 0;
}
输出:
$ time ./fibo 200000 100000 871925546 real 0m34.667s user 0m34.288s sys 0m0.113s $ time ./fibo-faster 200000 100000 871925546 real 0m15.073s user 0m14.846s sys 0m0.064s
鉴于输入值的限制:
T(n, k)
的值在[0..1000000006]的范围内,该值适合int32_t
。k
项的总和在[0..200000 * 1000000006]范围内,该范围适合int64_t
。这提供了更快的版本(快3倍以上):
#include <stdio.h>
#include <stdint.h>
#include <stdlib.h>
#define DIVIDER 1000000007
uint32_t fibo(uint32_t n, uint32_t k) {
uint32_t c = 1;
if (n > k) {
uint32_t *a = (uint32_t *)malloc(sizeof(*a) * k);
uint64_t temp;
for (uint32_t i = 0; i < k; i++) { //T(n,k)=1 when n<=k
a[i] = 1;
}
for (uint32_t m = k; m < n; m++) {
temp = a[0];
for (uint32_t j = 0; j < k - 1; j++) {
temp += a[j] = a[j + 1];
}
a[k - 1] = c = temp % DIVIDER;
}
free(a);
}
return c;
}
int main(int argc, char *argv[]) {
if (argc <= 2) {
printf("usage: fibo n k");
return 1;
} else {
uint32_t n = strtoul(argv[1], NULL, 10);
uint32_t k = strtoul(argv[2], NULL, 10);
printf("%lu\n", (unsigned long)fibo(n, k));
}
return 0;
}
输出:
$ time ./fibo-faster 200000 100000 871925546 real 0m3.854s user 0m3.800s sys 0m0.018s
答案 1 :(得分:0)
为避免溢出,您可以更改以下语句
c=c+*(a+j);
收件人
c=(c+*(a+j))%1000000007;
这意味着仅剩余部分将保留在您的堆中。这不会影响最终结果。
这是更新的代码,由clang编译。(根据@bruno的评论更新)
#include <stdlib.h>
#include <stdio.h>
#define DIVIDER 1000000007ul
#define U4 unsigned long
U4 fibo(U4 n,U4 k)
{
U4 *a,c ;
if(n<=k) return 1;
a= (U4*) malloc (sizeof(U4)*k);
for (U4 i=0;i<k;i++) //T(n,k)=1 when n<=k
{
*(a+i)=1;
}
for (U4 m=k;m<n; m++)
{
c=*(a);
for (U4 j=0;j<k-1;j++)
{
*(a+j)= *(a+j+1);
c=(c+*(a+j))%DIVIDER;
}
*(a+k-1)=c;
}
free(a);
return c;
}
int main(int argc, char *argv[])
{
U4 n, k;
char *endptr;
if(argc <= 2){
printf("usage: t.exe n k");
return 0;
}
n = strtoul(argv[1], &endptr, 10);
k = strtoul(argv[2], &endptr, 10);
printf("%lu", fibo(n,k));
}
编译器命令:
$ clang test.c -o test.exe
$ test.exe 200000 500
80391289