题目:传送门
解答:f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7
乍看之下像是递归。但是数据范围太大了,一定会超时。可以想到找规律。
如果将 f(n) 视为一个状态,那么决定它的是谁?是前两个状态。而且因为 mod 7,所以这个函数的定义域、值域都是{0,1,2,3,4,5,6}。
这样,我们可以构造一个 7*7的有限状态机(二维数组),每个状态填写出现的次数。当我们即将填写一个状态时发现里面已经出现了次数,当前次数 - 已有次数就是循环的规模。最多计算49次,我们一定会发现循环规律。
这里需要注意的是:
- 需要将输入的 n 减去刚刚发现规律时的状态机次数(正式开始循环),再对循环规模取模;
- 当取模 = 0时,应该加上循环规模。例如:1 1 4 2 4 2,如果直接按照取模计算,则 f(4) = 1 而不是 2;
- 无需判断 n 是否小于循环规模,因为有限状态机一定停机。即使小于对循环规模取模仍得到自己。
#include<stdio.h> #include<stdlib.h> #include<string.h> #include<string> #include<iostream> #include<vector> #include<map> using namespace std; int main() { int a, b, i, j; long n; int map[7][7]; while (cin>>a>>b>>n && a && b && n) { if(n == 1 || n == 2) { cout<<"1"<<endl; continue; } memset(map, 0, sizeof(map)); int val = 1; map[1][1] = val; i = 1; j = (a*1 + b*1) % 7; int k = 0; while(!map[i][j]) { val++; map[i][j] = val; int tmp = j; j = (a*j + b*i) % 7; i = tmp; // 多余操作,状态机一定会停机 //num--; //if (num == 0) //{ // cout<<j<<endl; // break; //} } int circle = (val + 1) - map[i][j]; int start = map[i][j]; n = (n - (start - 1)) % circle;; if(n == 0) n = circle; n = n + (start - 1); int ans; for (int k = 0; k < 7; k++) { for (int l = 0; l < 7; l++) { if (n == map[k][l]) { ans = k; } } } cout<<ans<<endl; continue; } return 0; }
时间: 2024-10-14 04:39:28