【更相减损法,dfs】Day 5 提高组模拟C组 T2 数字对

题目链接

https://www.luogu.org/problemnew/show/P2090

题目大意

给定 n ,有两种方式转换 ( a , b ) -> ( a + b , b ) ( a , b ) -> ( a , a + b ) ,求出 ( 1 , 1 ) -> ( n , x ) 的最小步骤, x 可以是任意正整数

解题思路

第一种方法,直接爆搜可以拿50分

第二种方法,反过来搜索加上剪枝也可以 A C ,就是让 ( a , b ) 可以转换成 ( a b , b ) ( a , a b ) ,然后递归
注意,该方法仅能在洛谷上AC,在JZOJ中会RE

第三种想法是基于第二种方法的倒序思想,用到了《九章算术》中提到的更相减损法(又称更相减损术),具体是这样的

g c d ( 2 a , 2 b ) = 2 g c d ( a , b )
g c d ( a , b ) = g c d ( a b , b ) | a > b
这里我们用到的是第二条,再配合上辗转相除法可以达到加速递推的效果
g c d ( a , b ) = g c d ( b , a % b )
带入进去 g c d ( b , a % b ) = g c d ( a b , b ) ,这样子就可以加速递推了。

因为题目要求的最终状态为 ( 1 , 1 ) ,所以当 g c d ( a , b ) = 1 的时候结束,而加速递推之后每次是进行 a     d i v     b 次运算

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);//输出
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/80985573
今日推荐