hdu2842 Chinese Rings 题解(矩阵快速幂)

本文探讨了一种类似于汉诺塔的递归游戏——连环问题。通过建立递推公式和转移矩阵,解决了求最少操作次数使序列全变为1的问题,并提供了详细的代码实现。

原题链接:
HDU

题意简述

给定一个 n n n连环,玩这个 n n n连环的规则:

  1. 初始 n n n个环都是 0 0 0
  2. 如果要动第 i i i个,那么第 i − 1 i-1 i1个要是 0 0 0,前 i − 2 i-2 i2个都要是 1 1 1
  3. 第一个随便动

最少动几次,能让这个序列都变成 1 1 1

数据

输入

多组数据。每次输入一个 n n n,输入 0 0 0结束。

输出

对于每个输入数据,输出答案。

样例

输入
1
4
0
输出
1
10

思路

看到Chinese我就来劲

和汉诺塔类似,是一个递归的游戏。我们考虑 f ( x ) f(x) f(x)为把前 x x x个都变一次的最小方案数,答案自然就是 f ( n ) f(n) f(n)了。那么,如何转移呢?

首先第 x x x个位置初始肯定是 0 0 0的。我们要把它变成 1 1 1,那么 x − 2 x-2 x2个都要变成 1 1 1。此时消耗 f ( x − 2 ) f(x-2) f(x2)步。然后此时我们再把第 x x x个位置变成 1 1 1,又用了 1 1 1步。会发现第 x − 1 x-1 x1个还是 0 0 0,怎么搞这个呢?
我们把前 x − 2 x-2 x2个变回去,变成 0 0 0。然后我们再变前 x − 1 x-1 x1个,就全都 1 1 1了。此时消耗步数 f ( x − 1 ) + f ( x − 2 ) f(x-1)+f(x-2) f(x1)+f(x2)步。

所以, f ( x ) = f ( x − 2 ) + f ( x − 2 ) + f ( x − 1 ) + 1 = f ( x − 2 ) + 2 f ( x − 2 ) + 1 f(x)=f(x-2)+f(x-2)+f(x-1)+1=f(x-2)+2f(x-2)+1 f(x)=f(x2)+f(x2)+f(x1)+1=f(x2)+2f(x2)+1

如何转移呢?我们设
[ f i f i − 1 1 ] ∗ [ ? ? ? ? ? ? ? ? ? ] = [ f i + 1 f i 1 ] = [ f i + 2 f i − 1 + 1 f i 1 ] \begin{bmatrix} f_i & f_{i-1} & 1 \end{bmatrix} * \begin{bmatrix} ? & ? & ?\\ ? & ? & ?\\ ? & ? & ?\\ \end{bmatrix} =\begin{bmatrix} f_{i+1} & f_i & 1 \end{bmatrix} =\begin{bmatrix} f_i+2f_{i-1}+1 & f_i & 1 \end{bmatrix} [fifi11]?????????=[fi+1fi1]=[fi+2fi1+1fi1]
先去确定第一列。显然应该是 [ 1 2 1 ] [1\quad 2\quad 1] [121]
同理确定出第二,三列分别是 [ 1 0 0 ] , [ 0 0 1 ] [1\quad 0\quad 0],[0\quad 0\quad 1] [100],[001]
然后就构造出了转移矩阵
[ 1 1 0 2 0 0 1 0 1 ] \begin{bmatrix} 1 & 1 & 0\\ 2 & 0 & 0\\ 1 & 0 & 1 \end{bmatrix} 121100001

然后转移即珂。特判 n = 1 , 2 n=1,2 n=1,2的情况,边界条件:
f 1 = 1 , f 2 = 2 f_1=1,f_2=2 f1=1,f2=2

代码:

#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
    #define mod 200907
    #define int long long
    class Matrix//square matrix
    {
        #define N 5//changeable
        private:
            int a[N][N];
        public:
            //variable list
            int n;//size
            //initialization
            Matrix()
            {
                memset(a,0,sizeof(a));
                n=0;
            }
            Matrix(int _n)
            {
                memset(a,0,sizeof(a));
                n=_n;
            }
            Matrix(int _n,int _x)
            {_x%=mod;
                n=_n;
                for(int i=0;i<N;++i)
                {
                    for(int j=0;j<N;++j)
                    {
                        a[i][j]=_x;
                    }
                }
            }

            //get value
            int* operator[](int i)
            {
                return *(a+i);
            }

            //set value
            void Set(int x)
            {x%=mod;
                for(int i=0;i<N;++i)
                {
                    for(int j=0;j<N;++j)
                    {
                        a[i][j]=x;
                    }
                }
            }
            void Identity()
            {
                memset(a,0,sizeof(a));
                for(int i=0;i<N;++i)
                {
                    a[i][i]=1;
                }
            }
            #undef N //5
    };
    Matrix operator*(Matrix x,Matrix y)
    {
        Matrix ans(x.n,0);
        int n=ans.n;
        for(int i=1;i<=n;++i)
        {
            for(int j=1;j<=n;++j)
            {
                for(int k=1;k<=n;++k)
                {
                    ans[i][j]+=x[i][k]*y[k][j];
                    ans[i][j]%=mod;
                }
            }
        }
        return ans;
    }
    Matrix operator^(Matrix x,int p)
    {
        Matrix ans(x.n,1);
        ans.Identity();
        while(p)
        {
            if (p&1) ans=ans*x;
            x=x*x,p>>=1;
        }
        return ans;
    }

    void calc(int x)
    {
        if (x==1)
        {
            puts("1");
            return;
        }
        if (x==2)
        {
            puts("2");
            return;
        }

        Matrix Trans(3,0);
        Trans[1][1]=Trans[1][2]=Trans[3][1]=Trans[3][3]=1;
        Trans[2][1]=2;

        Matrix Init(3,0);
        Init[1][1]=2;
        Init[1][2]=Init[1][3]=1;

        Matrix Ans=Init*(Trans^(x-2));
        printf("%lld\n",Ans[1][1]);
    }
    void IsMyWife()
    {
        if (0)
        {
            freopen("","r",stdin);
            freopen("","w",stdout);
        }
        int x;
        while(scanf("%lld",&x)==1 and x)
        {
            calc(x);
        }
    }
    #undef mod //200907
    #undef int //long long
};
int main()
{
    Flandle_Scarlet::IsMyWife();
    return 0;
}

回到总题解界面

评论 3
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值