HOJ 1005题 题解

      杭电OJ 1005题 题目链接:点击打开链接

      本题思路:给出的公式,第一个想到的是递归,使用递归实现,果然超内存,因为1 <= n <= 100,000,000(嘿嘿,我知道不用试也应该知道);接下来尝试使用栈的方式实现,超时了,至此偷懒不成功,开始寻找更好的方法吧。

     最终通过网上各种查找,再加上自己的思考和摸索,理解如下:

   (1)很明显f(n)的取值范围为0~6 

   (2)在A,B给定的情况下,f(n)的值由f(n-1)的值和f(n-2)的值决定

   (3)考虑数列的可能值,f(1)=1,f(2)=1,f(3)=*,f(4)=*,f(5)=* ....................f(49)=*,f(50)=*,f(51)=*,f(52)=*   ............ 。其中‘*’表示值为0~6之间的任意数。将f(1)f(2)这样某两个数放在一起,称为“数值对”(我自己取得名字,方便称呼而已),那么这个数列就可以构成下列的数值对f(1)f(2)、f(2)f(3)、f(3)f(4)、f(4)f(5)......f(49)f(50)、f(50)f(51)、f(51)f(52)等等。现在看这些数值对m1m2,由于m1、m2的取值范围为0~6,那么m1m2数值对的可能情况一共有49种,即00、01、02、04、05、06、10、11、12、13、14、15、16、20.....64、65、66共49(7*7)种。再看数值对f(1)f(2)、f(2)f(3)、f(3)f(4)、f(4)f(5)......f(49)f(50)、f(50)f(51)、f(51)f(52).....,到f(49)f(50)这里时,刚好是第49个数值对,所以说f(50)f(51)一定会和f(1)f(2)~f(49)f(50)中的某个数值对是相同的(因为数值对的所有可能一共是49个嘛,所以最晚到这个时候已经开始出现重复了,当然也可能从f(50)f(51)之前的某个数值对就已经重复了)。

  (4)至此我们可以确定,f(50)f(51)一定会和f(1)f(2)~f(49)f(50)中的某个数值对是相同的,也就是说f(50)会等于f(n),n为0~49之间的某个数,而f(51)等于f(n+1)。因为在A、B确定的情况下,f(n)的值由f(n-1)和f(n-2)确定,那么f(n+2)的值也由f(n+1)和f(n)决定,那么我们就找到了一个循环节,也就是f(n)f(n+1)f(n+2)....和f(50)f(51)f(52)...是相同的序列。而50-n的值即为循环周期。

  (5)找到循环周期和循环节之后,问题就可以简化了,假设m是个很大的数,f(m)一定会和f(1)~f(49)中的某个数相等。

  (6)看了网上的各种题解之后,我的一些个人理解:我不认为循环周期一定是49,也不认为开始产生循环的那个数值对一定是“11”。列如,在a=10,b=21时,就会发现数列的循环周期是48,最开始的11数值对就只出现了一次,整个数列是从f(2)f(3)开始出现循环的。该情况的测试代码也会在最后给出。


   本题AC参考代码:

  

import java.io.BufferedInputStream;
import java.util.Scanner;
import java.util.Stack;

public class Main {
	public static int fun(int a, int b, int n) {
		// 使用栈保存中间结果,计算f(n)的值
		Stack<Integer> nums = new Stack<Integer>();
		nums.push(1);
		nums.push(1);
		int result = 0;
		for (int i = 3; i <= n; i++) {
			int f1 = nums.pop();
			int f2 = nums.pop();
			nums.push(f1);
			result = (a * f1 + b * f2) % 7;
			nums.push(result);
		}
		return result;
	}

	public static void main(String[] args) {
		Scanner cin = new Scanner(new BufferedInputStream(System.in));
		int a = cin.nextInt();
		int b = cin.nextInt();
		int n = cin.nextInt();
		while (!(a == 0 && b == 0 && n == 0)) {
			// 首先计算出f(1)~f(49)的值,用数组nums[]保存
			int[] nums = new int[49];
			nums[0] = 1;
			nums[1] = 1;
			for (int i = 2; i < 49; i++) {
				nums[i] = fun(a, b, i + 1);
			}

			// 如果n<=49,直接输出
			if (n <= 49) {
				System.out.println(nums[n - 1]);
			} else {
				// 计算f(50)、f(51),寻找循环周期
				int f50 = fun(a, b, 50);
				int f51 = fun(a, b, 51);
				int period = 0;
				// 遍历nums[]数组,寻找循环周期。在下列循环中一定会找到循环,因为一定会重复
				// 具体原因请看上面的题解部分
				for (int i = 0; i < 49; i++) {
					int fn = nums[i];
					int fn1 = nums[i + 1];   // 这个变量的取名其实是想表示f(n+1)的意思...
					if (fn == f50 && fn1 == f51) {
						// 找到循环周期
						// 从f(i+1)~f(49)即为一个循环节
						period = 50 - (i + 1);
						break;
					}
				}

				// 开始计算f(n),关键是计算出f(n)=f(?)
				int subscript = n % period;
				//防止数组下标越界,因为下面有nums[subscript-1]
				if(subscript == 0){
					subscript = period;
				}
				int result = nums[subscript-1];
				System.out.println(result);
			}
			a = cin.nextInt();
			b = cin.nextInt();
			n = cin.nextInt();
		}
		cin.close();
	}
}


   标号(6)中所提到的测试程序如下:

import java.util.Stack;

public class Test {

	public static int fun(int a, int b, int n){
		if(n==1 || n==2){
			return 1;
		}
		//使用栈保存中间结果,计算f(n)的值
		Stack<Integer> nums = new Stack<Integer>();
		nums.push(1);
		nums.push(1);
		int result = 0;
		for(int i=3; i<=n; i++){
			int f1 = nums.pop();
			int f2 = nums.pop();
			nums.push(f1);
			result = (a*f1 + b*f2) % 7;
			nums.push(result);
		}
		return result;
	}
	
	public static void main(String[] args) {
		int a,b,n;
	    a = 10;
	    b = 21;
	    n = 300;
		for(int i=1; i<=n; i++){
			int result = fun(a,b,i);
			System.out.print("a=" + a+"   " + "b=" + b + "  ");
			System.out.println("f(" + i + ")=" + result);
		}

	}
}

      欢迎评论指正讨论,不喜勿喷。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值