HDU1005解析

题目大意:
有这样一个数列f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7.给我们a和b求f(n)的值。
题目分析:
试错分析
这是一道很有意思的题目本着做完之后去看看别人解析的习惯我发现大多数的分析情况都考虑的不够全面所以就写下了这篇博客。
试错:看到这个题目的第一眼首先想到了,直接递归求解,看了一眼数据大小明显递归会炸,于是我又考虑记忆化搜索,但是n的大小在10^8很明显如果采用记忆化搜索这道题一定会炸,所以肯定也不行。
于是看了看题目的形式 f(n) = (A * f(n - 1) + B * f(n - 2)) mod 7通过膜公式可知我们推到的排列最多有49种可能,所以这个fn肯定是一个循环的数列,于是我当场打了个表验证了一下。好既然他是循环的那肯定有循环的节点 很容易想到的是该数列如果是循环的,那么他从任意一处节点出发,都会回到他的起点按照这一思路,我推测他的数据应该是以1,1结尾的数据于是我提交了我的第一次代码,没有想象中的AC居然是WA。
正确分析:
很明显在部分情况下他的循环不是(1,1),在观察上面的式子,我发现当A为0或者B为0的情况下这个式子就只有一个数字在加,这种情况下他的结果应该是以某一个数开始循环在通过一系列的推导因为B是第二个所以B为0的循环应该是一个以1开始的循环,而A为0的循环应该是一个AABB这样形式的一个循环那么这种情况下还是会回到1 1的循环所以不用考虑,于是问题就变得简单起来了,加入一个b为0的特判就可以了。
其中这道题有个难点就是如果一次求fi的话虽然也可以得到结果但他的时间复杂度是O(n)而题目有要求多组输入,看了眼题目的数据如果这么做的话如果可以AC也是很勉强的。那么我们要想办法尽量将时间复杂度降下来因为他是循环的所以我们只要将n对循环链取模就可以得到我们想要的答案,同时因为我们知道循环链最大的长度是49,所以他的时间复杂度为O(1),当然了难点还是在于b怎么去取模,这个时候我们只要将b的最后一个放到最前面也就是原来f1的位置上就可以实现循环链了。
AC代码如下:

#include<iostream>
#include<cstring>
using namespace std;
int a,b;
bool map[10][10] ={0};//因为取模所以f1和f2的值只会在0-6之间
int dp[50] ={0};//根据排列组合总共有49总可能
int cnt;//记录循环长度
int func(int n)
{
    dp[0] = dp[1] = 1;//初始化
    if(n==1 || n==2)
        return 1;
    if(a==0 && b==0)
        return 0;//当a和b均为0的时候 f3 到 fn均为0
    int f1 = 1,f2 = 1;
    while(!map[f1][f2]) //map[f1][f2] 表示f(n-1) =f1 ,f(n-2) = f2 的值
    {
        dp[cnt] = (a*f1 + b*f2)%7;
        map[f1][f2] = true;//标记有这个结果 用于判断前面是否出现该情况若出现则遇见循环
        f2 = f1;
        f1 = dp[cnt++];
    } 
    if(b==0)
    {
        if(a==1)
            return 1;
        dp[0] = dp[cnt-3];//当遇到b==0的时候,将最后一位放到第一位,建立一个新的循环
        return dp[(n-1)%(cnt-3)];
    }
    return dp[(n-1)%(cnt-2)];
}
int main()
{
    int n;
    while (cin>>a>>b>>n)
    {
        if(n==0)
            return 0;
        memset(map,0,sizeof(map));
        memset(dp,0,sizeof(dp));
        cnt = 2;
        a%=7;
        b%=7;
        //根据膜公式 先对a,b进行取模;
        cout<<func(n)<<endl;
    }   
    return 0;
}

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值