HDU2842 Chinese Rings

HDU2842(不水)

英文版原题

题目描述:一根木棒上有n个环(n<=10^9) 第一个环可以随意取下或者放上 如果前k个环都不在棒子上,且第k+1个环在棒子上,则你可以取下或放上第k+2个环 给出n,求最少需要多少步可以取完棒子上的环?

思路:

  1. 设f[n]数组表示取下n个环所需最小次数;
    1. 若想让第n个环被取下,那么前(n-2)个都要被取下,第(n-1)要挂在环上;这时所需次数为f[n-2]+1;
    2. 考虑第(n-1)个环还未取下;而取下第(n-1)个环需要第(n-2)个环挂上,取下第(n-2)个环需要第(n-3)个环挂上…即先要把前(n-2个都取下来);还有f[n-1]它自己;以此类推,取下第(n-1)个环需要次数为f[n-2]+f[n-2];
    3. 合并得到递推公式为:f[n] = f[n-1] + 2 * f[n-2] + 1;

然后有个矩阵相乘,每次换值(有点像DP)

矩阵乘法 规则+理解

菲波那切数列的递推理解
菲波那切数列的递推理解1
菲波那切数列的递推理解2

类似于斐波那契,本题的递推公式为
A:
| f[n] |
| f[n-1] |
| 1 |
B:
| 1 2 1 |
| 1 0 0 |
| 0 0 1 |
C:
| f[n-2]|
| f[n-1]|
| 1 |

A=B*C
就这样
贴上翻找到的 AC代码:
大神的代码

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define size 3
#define mod 200907
struct Mat{long long num[size][size];};
Mat init,r;//定义全局变量;

void InitMat()//初始化全局变量函数
{
    int i,j;
    for (i=0;i<size;i++)
        for (j=0;j<size;j++)
            r.num[i][j] = init.num[i][j] = 0;
            r.num[1][0] = r.num[2][0] = init.num[0][0]=init.num[0][2]=init.num[1][0]=init.num[2][2]=1;
            r.num[0][0] = init.num[0][1]= 2;
}

Mat mul(Mat m,Mat r)//矩阵相乘
{
    Mat c;
    memset(c.num,0,sizeof(c.num));
    for (int i=0;i<size;i++)
    {
        for (int j=0;j<size;j++)
        {
            c.num[i][j] = 0;
            for(int k=0;k<size;k++)
            c.num[i][j]+=(m.num[i][k]*r.num[k][j])%mod;
            c.num[i][j]%=mod;
        }//矩阵相乘并赋给ans

    }
    return c;//返回最后的值
}

Mat pow(Mat m,int k)//矩阵的乘方函数
{
    Mat ans;
    memset(ans.num,0,sizeof(ans.num));//首先置为0
    for(int i=0;i<size;i++)
        for(int j=0;j<size;j++)
            if(i==j) ans.num[i][j]=1;//置为单位矩阵
    while(k)
    {
        if(k&1) ans = mul(ans,m);//开始矩阵的乘方
        k >>= 1;
        m = mul(m,m);
    }
    return ans;//返回所求矩阵
}


int main()
{
    int n;
    InitMat();
    while (scanf("%d",&n)!=EOF)
    {
        if(n==0) return 0;
        if(n==1) printf("1\n");
        else if(n==2) printf("2\n");
            else
            {
                Mat multi=pow(init,n-2);
                Mat t=mul(multi,r);
                printf("%lld\n",t.num[0][0]);
            }
    }
    return 0;
}

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值