原题链接:
HDU
题意简述
给定一个 n n n连环,玩这个 n n n连环的规则:
- 初始 n n n个环都是 0 0 0
- 如果要动第 i i i个,那么第 i − 1 i-1 i−1个要是 0 0 0,前 i − 2 i-2 i−2个都要是 1 1 1。
- 第一个随便动
最少动几次,能让这个序列都变成 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
x−2个都要变成
1
1
1。此时消耗
f
(
x
−
2
)
f(x-2)
f(x−2)步。然后此时我们再把第
x
x
x个位置变成
1
1
1,又用了
1
1
1步。会发现第
x
−
1
x-1
x−1个还是
0
0
0,怎么搞这个呢?
我们把前
x
−
2
x-2
x−2个变回去,变成
0
0
0。然后我们再变前
x
−
1
x-1
x−1个,就全都
1
1
1了。此时消耗步数
f
(
x
−
1
)
+
f
(
x
−
2
)
f(x-1)+f(x-2)
f(x−1)+f(x−2)步。
所以, 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(x−2)+f(x−2)+f(x−1)+1=f(x−2)+2f(x−2)+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}
[fifi−11]∗⎣⎡?????????⎦⎤=[fi+1fi1]=[fi+2fi−1+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;
}
本文探讨了一种类似于汉诺塔的递归游戏——连环问题。通过建立递推公式和转移矩阵,解决了求最少操作次数使序列全变为1的问题,并提供了详细的代码实现。
16万+





