從HD OJ 1005想到的

杭電OJ [1005](http://acm.hdu.edu.cn/showproblem.php?pid=1005):

#####Problem Description
> A number sequence is defined as follows: f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7. Given A, B, and n, you are to calculate the value of f(n).

#####Input
> The input consists of multiple test cases. Each test case contains 3 integers A, B and n on a single line (1 <= A, B <= 1000, 1 <= n <= 100,000,000). Three zeros signal the end of input and this test case is not to be processed.

#####Output
> For each test case, print the value of f(n) on a single line.

#####Sample Input
> 1 1 3
1 2 10
0 0 0

#####Sample Output
> 2
5

題目對於那些ACM選手來講,確定不是什麼大問題,不過對於咱們這種只能刷刷水題的人來講,仍是有點困難的。

我看到題目以後的第一反應是對於每個特定的A和B,每次的計算結果都存到數組裏,這樣不用每一個輸入都從新計算,能夠節省必定量的時間,當時以爲這個想法已經不錯了,可是仍是TLE了。最後上網找了答案,發現不少答案裏都提到f(n)的值實際上是循環的,並且最大的循環長度不會超過49(即起碼在n=49以前,f(n)的值就開始循環了,f(k)=f(1),f(k+1)=f(2),f(k+2)=f(3),...,k<=49)。可是網上不少文章並無指出怎麼才能發現,或者說推導出這個規律。最後我花了點時間,本身推了下才終於知道了發現規律的方法。

首先,觀察到遞推式裏有mod 7,就知道f(n)的全部值都在[0, 6]之間,有7個取值。

而後,觀察遞推式,f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7,A和B是定常數,而f(n - 1)和f(n - 2)的取值都分別有7種可能,也就是說f(n)的取值最多有49種組合(這49種組合中,和可能相同,可是表明的意義不一樣,例如1+4和2+3是不一樣的,2+3和3+2也是不一樣的)。當n > 51時(由於這種組合是從n=3開始算的),f(n)的取值組合必然是以前出現過了的,也就是必存在
```
f(n) = f(k), f(n - 1) + f(n - 2) = f(k - 1) + f(k - 2)
f(n - 1) = f(k - 1)
f(n - 2) = f(k - 2)
```
可是其實咱們能夠知道,f(n) = 0 + 0,這種狀況是不可能的(由於這樣話很容易證實對於全部的n,f(n)都是爲0),因此其實最多隻有48種狀況,即從n = 50開始,組合就必然出現重複了。
其次,咱們知道了f(n)和f(k)的取值組合徹底相同後,只要證實f(n + 1) = f(k + 1)的便可證實f(n)的取值在n > 49後必然存在循環。
```
f(n + 1) = f(n) + f(n - 1)
f(n) = f(k)
f(n - 1) = f(k - 1)
```
由以上條件可知,f(n + 1) = f(k + 1),同理能夠推導出f(n + 2) = f(k + 2),...,等等。並最終證實f(n)的值是循環的。

最後,咱們已經證實了f(n)是存在循環的,最後要證實的是f(n)是整循環的,也就是說循環起始點應該是f(n) = f(n - 1) + f(n - 2) = f(1) + f(2)。先假設f(n + 2) = f(5) = f(n + 1) + f(n) = f(4) + f(3),n是循環開始點,由假設能夠推導出f(n + 1) = f(n) + f(n - 1) = f(4) + f(3),f(n - 1)和f(3)以前存在兩種可能:
1. f(n - 1) = f(3)
2. |f(n - 1) - f(3)| = 7
由f(n)的取值範圍[0, 6]知,第二種狀況是不可能的,因此f(n - 1) = f(3),因此n - 1是循環開始點,依次能夠類推n - 2是循環開始點,...,直到f(n - k) = f(3) = f(n - k - 1) + f(n - k - 2) = f(2) + f(1),n - k - 2是循環開始點。因此由證實可知,若是n是循環開始點,則f(n) = f(1),f(n + 1) = f(2),...。

知道了f(n)的值是循環的以後,這道題目就很容易作了,只要求出循環開始點就好了,即f(i - 1) = 1, f(i) = 1。

具體實現代碼以下:

#include <iostream>
using namespace std;

int f[50] = {0, 1, 1};
int a, b, n;
int main() {
    while (cin >> a >> b >> n) {
        if (a == 0 && b == 0 && n == 0) {
            break;
        }
        if (n > 2) {
            int i;
            for (i = 3; i <= 49; i++) {
                f[i] = (a * f[i - 1] + b * f[i - 2]) % 7;
                if (f[i] == f[2] && f[i - 1] == f[1]) {
                    break;
                }
            }
            i -= 2;
            n = n % i == 0 ? i : n % i;
        }
        cout << f[n] << endl;
    }
}
相關文章
相關標籤/搜索