题目链接
https://www.luogu.org/problemnew/show/P2090
题目大意
给定 ,有两种方式转换 -> 或 -> ,求出 -> 的最小步骤, 可以是任意正整数
解题思路
第一种方法,直接爆搜可以拿50分
第二种方法,反过来搜索加上剪枝也可以
,就是让
可以转换成
和
,然后递归
注意,该方法仅能在洛谷上AC,在JZOJ中会RE
第三种想法是基于第二种方法的倒序思想,用到了《九章算术》中提到的更相减损法(又称更相减损术),具体是这样的
这里我们用到的是第二条,再配合上辗转相除法可以达到加速递推的效果
带入进去
,这样子就可以加速递推了。
因为题目要求的最终状态为 ,所以当 的时候结束,而加速递推之后每次是进行 次运算
dfs代码(50分)
#include<cstdio>
#define swap(a,b) {a^=b;b=a^b;a^=b;}
using namespace std;int n,ans=2147483647,f[1000001];
void dfs(int a,int b,int dep)
{
if(a<b) swap(a,b);
if(a>n||b>n) return;
if(a==n||b==n) {if(dep<=ans)ans=dep;return;}//保存答案
if(f[a]==b||f[b]==a) return;
f[a]=b;//一个小剪枝,然而并没有什么用
dfs(a,a+b,dep+1);
dfs(a+b,b,dep+1);
}
int main()
{
scanf("%d",&n);
dfs(1,1,0);
printf("%d\n",ans);
}
dfs代码(100分)
#include<cstdio>
#define min(a,b) a<b?a:b
using namespace std;int n,ans=2147483647,f[1000001];
void dfs(int a,int b,int dep)
{
if(dep>ans) return;//最优化原理
if(a==1&&b==1) {ans=min(ans,dep);return;}
if(a<1||b<1) return;
if(a>b) dfs(a-b,b,dep+1);
if(b>a) dfs(a,b-a,dep+1);
}
int main()
{
scanf("%d",&n);
for(int i=2;i<n;i++)
dfs(n,i,0);
printf("%d",ans);//输出
}
AC代码
#include<cstdio>
#define min(a,b) a<b?a:b
using namespace std;int n,ans=1e9;
int gcd(int a,int b)
{
if(b==1) return a;//结束,返回a
if(!b) return 1e9;
return gcd(b,a%b)+a/b;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++) ans=min(ans,gcd(n,i));//保存最优解
printf("%d",ans-1);//输出
}