杭电1005思考过程

杭电1005是以下这样的一道题:

问题描述
一个数字序列定义如下:

f(1)= 1,f(2)= 1,f(n)=(A * f(n-1)+ B * f(n-2))mod 7.

给定A,B和n,您将要计算f(n)的值。

输入项
输入包含多个测试用例。每个测试用例在一行上包含3个整数A,B和n(1 <= A,B <= 1000、1 <= n <= 100,000,000)。三个零表示输入结束,该测试用例将不被处理。

输出量
对于每个测试用例,将f(n)的值打印在一行上。

样本输入
1 1 3
1 2 10
0 0 0

样本输出
2
5

这道题,我这种小白踩了很多坑…
问题解答过程:
我第一眼看过去,就试着用递归解答。
于是我写了以下代码:

递归法

#include<stdio.h>
int main(){

	int dg(int a,int b,int n);
	int a,b,n,sum;

	while(scanf("%d %d %d",&a,&b,&n)!=EOF){
		if(a==0&&b==0&&n==0) break;
    sum = dg(a,b,n);
   printf("%d\n",sum);
	}
	return 0;

}

int dg(int a,int b,int n){
    if(n==1||n==2) return 1;
	else 
	return (a * dg(a,b,n - 1) + b * dg(a,b,n-2)) % 7;

}

}

提交结果当然是没能通过,报的错是Memory Limit Exceeded(空间超出限制)

于是我试着改成用递推法

#include<stdio.h>
int c[100000001];
int main(){

	int a,b,n,i;
	while(scanf("%d %d %d",&a,&b,&n)!=EOF){
		if(a==0&&b==0&&n==0) break;
         
		c[0] = 1;
		c[1] = 1;
		for(i =2 ;i<n;i++)
        c[i] = (a*c[i-1]+b*c[i-2])%7;

     printf("%d\n",c[n-1]);
	}
	return 0;

}

结果可想而知,也是Memory Limit Exceeded(空间超出限制)。我脑子真的秀逗了。递归和递推都是占用大量空间的,何况题目n可以等于100000000那么大。

于是我在一步改进算法:
用递推迭代

#include<stdio.h>
int main(){

	int a,b,n,i;
	int one,two,three;
	while(scanf("%d %d %d",&a,&b,&n)!=EOF){
		if(a==0&&b==0&&n==0) break;
         
		one = 1;
		two = 1;
		for(i =2 ;i<n;i++){
        three = (a*two+b*one)%7;
		one = two;
		two = three;
		}
     printf("%d\n",two);
	}
	return 0;

}

结果还是没能accepted,这次是时间超出限制。。。

分析:
1 <= n <= 100,000,000
就是说可能要迭代计算100000000次,超时也正常,f(n)的值也必须依赖f(n-1)和f(n-2),这样还是无论如何都无法避免前面的计算。
那必定不是一步步递推计算得来,应该存在规律。观察发现每次的值都是0-6即{0,1 ,2,3,4,5,6},是否会存在循环呢?如果存在循环就好办,算法时间复杂度可从O(n)变O(1)。

再分析
f(n)=(A * f(n-1)+ B * f(n-2))mod 7

因为A,B是固定的值,f(n-1)和f(n-2)决定f(n)的值,那么只有f(n-1)和f(n-2)的值重复出现一次,那么必定会循环。而f(n-1)和f(n-2)的取值都是0-6,根据组合出来的结果是7*7=49种,也就是说最坏的情况是第50次才重复一次,那最坏周期就是49.

以下是能accepted的代码:

#include<stdio.h>//规律
int main(){

    int a,b,n,i;
    int flag[50];
    while(scanf("%d %d %d",&a,&b,&n)!=EOF){
        if(a==0&&b==0&&n==0) break;
        flag[1] = 1;
        flag[2] = 1;
        for(i =3 ;i<=49;i++){
        flag[i] = (flag[i-1]*a+flag[i-2]*b)%7;
        }

     printf("%d\n",flag[n%49]);
    }
    return 0;

}

我只想到必定存在规律,没找到周期,最后是参考网上大神才知道周期是49,只能说,不容易,脑子不行。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值