这个题并不是什么难题,写这篇题解的原因是现在CSDN上的大部分%50,%49,%48的题解原理不太对,hdu此题的测试数据比较水,经过探索我发现了网上很多ac代码并过不了一些特殊的数据。因此,在这里做个我认为最正确的题解。
首先讲述一下抽屉原理在此题的应用:
很显然,根据题意,f[]数组是一个由0-6七个数字组成的数组,我们现在只考虑 f[i] 和 f[i+1] 两个相邻元素,我们现在把这对数字叫做相邻。那么让我们来考虑一下这些相邻配对可能出现的情况。可能是0,0、0,1、0,2、……很显然,它们只可能是0-6的排列,这一对数字只可能有49种情况。由于没有第50种出现的可能,在f数组前51个数中(前51个数有50组相邻配对),一定会出现两组一样的相邻配对。又因为每个元素只与它的前面两个元素有关系,因此必定在前51个数种会产生循环。
然而,与网上大多数题解所认为的循环节(也就是一个循环单位)长度一定是49的因数相违背的是,当a=2,b=7时,f的情况是 1,1,2,4,1,2,4,1,2,4……,在这种情况下循环节为3,且循环节并没有包括开头的 1,1 ,因此当输出 n=52时,它们得到的输出往往是 2,并不是正确答案 4。这一点由于oj数据太水让他们ac了。
针对这种特殊情况,我们可以得到以下结论:
1、循环节不一定包括开头所给的初始值 1,1
2、循环节的长度我无法预测有什么规律
3、开始循环的起点我也无法预测
那么这个问题就转化成:我需要一边计算f的值,一边寻找是否已出现循环点(也就是出现了两对相同的相邻配对),我需要找出循环的起点,循环节的长度。
由于我们已经知道了前51个数肯定会出现循环,那我们暴力就行了,时间肯定够的不要虚,最后时间复杂度最坏是O(50*50),也就是O(1)嘛,不虚不虚,每计算出一个新的 f[i],我就向前搜一下,有没有出现过 f[i] 与 f[i-1] 组成的循环配对。
上代码
#include<iostream>
#include<vector>
#include<stdio.h>
#include<cmath>
#include<string>
#include<string.h>
#include<limits>
#include<limits.h>
#include<algorithm>
#include<queue>
#include<set>
#include<bits/stdc++.h>
#include<map>
using namespace std;
int f[100];
int length,st;//循环节长度和循环开始的标记
bool finda(int n){
int a=f[n-1],b=f[n];
for(int i=1;i<n-1;i++){
if(f[i]==a&&f[i+1]==b){
st=i;
length=n-1-i;
return true;
}
}
return false;
}
int main(){
int a,b,n;
while(1){
cin>>a>>b>>n;
if(a==0)break;
f[1]=1;f[2]=1;
for(int i=3;i<100;i++){
f[i]=(a*f[i-1]+b*f[i-2])%7;
if(finda(i))break;
}
if(n<st)cout<<f[n]<<endl;
else cout<<f[(n-st)%length+st]<<endl;
}
}
另附一些能ac代码确不能通过的数据,只有2 7 52是我自己找到的,剩下的是贴的hdu discuss 区的http://acm.hdu.edu.cn/discuss/problem/post/reply.php?postid=34360&messageid=6&deep=2
2 7 52
247 602 35363857
376 392 9671521
759 623 18545473
53 399 46626337
316 880 10470347
0 0 0
4
4
3
5
2
3