hiho一(第四十一周)------骨牌覆盖问题·一

时间限制: 10000ms
单点时限: 1000ms
内存限制: 256MB
描述

骨牌,一种古老的玩具。今天我们要研究的是骨牌的覆盖问题:
我们有一个2xN的长条形棋盘,然后用1x2的骨牌去覆盖整个棋盘。对于这个棋盘,一共有多少种不同的覆盖方法呢?
举个例子,对于长度为1到3的棋盘,我们有下面几种覆盖方式:

提示:骨牌覆盖

提示:如何快速计算结果

输入

第1行:1个整数N。表示棋盘长度。1≤N≤100,000,000

输出

第1行:1个整数,表示覆盖方案数 MOD 19999997

样例输入
62247088
样例输出
17748018

分析:

1、其实没有什么要分析的,因为官方给的提示已经够详细了(强烈推荐)。

2、总结一下:像斐波那契数列那种递归思路题,有许多解法。但是,时间复杂度上有质的飞越。下面我们就来分析分析:


(1)、首先,一般思路递归方法-----递归函数。

#include <iostream>
using namespace std;

int f(int n)
{
    if(n<3)
        return 1;
    return f(n-1)+f(n-2);
}

int main()
{
    int n;
    cin>>n;
    cout<<f(n)<<endl;
    return 0;
}


(2)、这种递归运行时是最慢的,最不可取的。不能算出较大的n。接下来较好的就是数组递归。

#include <iostream>
using namespace std;

int main()
{
    int n;
    cin>>n;
    long long a[100005]={1,1};
    for(int i=2;i<n;i++)
        a[i]=a[i-1]+a[i-2];
    cout<<a[n-1]<<endl;
    return 0;
}


(3)、数组递归相对于递归函数有了显著提高,但还有比数组递归更好,更快的。那就是记忆化递归。做hihoCoder这道题时,开始我就是用这种方法,肯定会超时。最主要的是无法计算n的18次方。

#include <iostream>
using namespace std;

const int mod=1000000;
long long used[1000005];

long long f(int n)
{
    if(used[n])
        return used[n]%mod;
    if(n<3)
        return 1;
    return used[n]=f(n-1)%mod+f(n-2)%mod;
}

int main()
{
    int n;
    cin>>n;
    long long ans=f(n);
    cout<<ans<<endl;
    return 0;
}

贴上我一开始做这道题的代码。

#include <iostream>
using namespace std;

typedef long long LL;
LL n,dp[1000005];
const LL MOD=19999997;

LL cal(LL x)
{
    if(x<1000000&&dp[x])
        return dp[x]%MOD;
    if(x==1)
        return 1;
    if(x==2)
        return 2;
    if(x<1000000)
        return dp[x]=cal(x-1)%MOD+cal(x-2)%MOD;
    else
        return cal(x-1)%MOD+cal(x-2)%MOD;
}

int main()
{
    cin>>n;
    LL sum=cal(n)%MOD;
    cout<<sum;
    return 0;
}


(4)最后,就是本道题 所提示的思路算法,矩阵乘法+快速幂。本题AC码:

LANGUAGE:C++

CODE:

#include <iostream>
using namespace std;

typedef long long LL;
LL n,M[4]={0,1,1,1},tmp[4]={1,0,0,1},t[4];
const int MOD=19999997;

void cal(LL a[],LL b[])
{
    t[0]=a[0]*b[0]+a[1]*b[2];
    t[1]=a[0]*b[1]+a[1]*b[3];
    t[2]=a[2]*b[0]+a[3]*b[2];
    t[3]=a[2]*b[1]+a[3]*b[3];
    return ;
}

int pow_quick(LL nn)
{
    while(nn){
        if(nn&1){
            cal(tmp,M);
            for(int i=0;i<4;i++)
                tmp[i]=t[i]%MOD;
        }
        cal(M,M);
        for(int i=0;i<4;i++)
            M[i]=t[i]%MOD;
        nn>>=1;
    }
    return 0;
}

int main()
{
    cin>>n;
    pow_quick(n);
//    for(int i=0;i<4;i++)
//        cout<<tmp[i]<<endl;
    cout<<tmp[3]%MOD;
    return 0;
}







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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值