假设初始位置为 (1, 1),请在每一步通过以下给定规则找出是不是可以到达 (x, y) [关闭]

Posted

技术标签:

【中文标题】假设初始位置为 (1, 1),请在每一步通过以下给定规则找出是不是可以到达 (x, y) [关闭]【英文标题】:Assuming initial position is (1, 1), find out if you can reach (x, y) by the following given rules at each step [closed]假设初始位置为 (1, 1),请在每一步通过以下给定规则找出是否可以到达 (x, y) [关闭] 【发布时间】:2021-01-06 00:49:30 【问题描述】:

给定坐标 (x,y)。最初,您在 (1,1) 和 需要使用以下规则前往 (x,y):如果当前 位置是 (a,b) 然后在下一步移动中,您只能移动到 (a+b,b) 或 (a,a+b)。编写一个程序来检查你是否可以到达 (x,y) 使用 只有描述的动作。

我试图通过递归来解决我上面提到的Java中的这个问题。但是该程序不适用于某些测试用例。我找不到问题所在。但我知道这是一种蛮力方法。

import java.util.*;
class Solution
    static boolean sol(int a, int b, int x, int y)
        if( a == x && b == y)
            return true;
        else if(a > x || b > y)
            return false;
        else
            if(sol(a+b, b, x, y)) return true;
            if(sol(a, a+b, x, y)) return true;
        
        return false;
    
    static String ans(int x, int y)
        if(sol(1, 1, x, y)) return "Yes";
        return "No";
    
    public static void main(String[] args) 
        int x, int y;
        Scanner sc = new Scanner(System.in);
            x = sc.nextInt();
            y = sc.nextInt();
            System.out.println(ans(x, y));
        
    
    

这是我写的代码。有人可以告诉我解决这个问题的效率并告诉我我的代码有什么问题吗?

【问题讨论】:

这和 Python 有什么关系? 你能展示一些简单的案例,其中问题没有给出正确的输出吗?什么是正确的输出,你会得到什么? 【参考方案1】:

你的问题是:

如果当前位置是(a,b),那么接下来的移动只能移动到(a+b,b)或者(a,a+b)。

您的错误是(以粗体突出显示的内联):

    if(sol(a+b, <strong>a</strong>, x, y)) return true; // a should be b if(sol(a, a+b, x, y)) return <strong>false</strong>; // false should be true

用(粗体突出显示的内联)校正两个点

    if(sol(a+b, <strong>b</strong>, x, y)) return true; if(sol(a, a+b, x, y)) return <strong>true</strong>;

或通过组合它们来简化: if(sol(a+b, <strong>b</strong>, x, y) || sol(a, a+b, x, y)) return true;

根据上述建议,完整代码为:

import java.util.*;
class Solution 
    static boolean sol(int a, int b, int x, int y) 
        if(a == x && b == y)
            return true;
        else if(a > x || b > y)
            return false;
        else if(sol(a+b, b, x, y) || sol(a, a+b, x, y)) // fix applied here
            return true;
        return false;
    
    static String ans(int x, int y) 
        if(sol(1, 1, x, y)) return "Yes";
        return "No";
    
    public static void main(String[] args) 
        int x, int y;
        Scanner sc = new Scanner(System.in);
        x = sc.nextInt();
        y = sc.nextInt();
        System.out.println(ans(x, y));
    


编辑优化解决方案而不是蛮力

我们从(1, 1) 开始,(a, b) 的任何下一步都是(a+b, b) or (a, a+b),所以x &gt; 0 and y &gt; 0 是任何步骤(x, y) 的必需条件。

现在,让我们说,

Step k:   (a, b)  
Step k+1: (a2, b2) = (a+b, b) OR (a, a+b)

因此,如果我们想从step#k+1 派生step#k,那么我们可以说(a, b) 可以是这两个之一:(a2 - b2, b2) OR (a2, b2 - a2)。我们可以轻松删除一个选项,因为我们知道a &gt; 0 and b &gt; 0

如果(a2 - b2) &gt; 0(a, b) 必须是(a2 - b2, b2)。 同样,如果(b2 - a2) &gt; 0(a, b) 必须是(a2, b2 - a2)。 如果a2 == b2(而不是1)则a2 - b2b2 - a2 将是0,这是不可能的,因为a &gt; 0 and b &gt; 0 是必需的条件。

因此,我们将从目的地(x, y) 开始,并尝试通过上述观察在线性时间 到达(1, 1)。要点是:

static boolean solve(int x, int y) 
    if (x == 1 && y == 1) 
        return true;
    
    if (x == y || x < 1 || y < 1) 
        return false;
    
    if (x > y) 
        return solve(x - y, y);
     else 
        return solve(x, y - x);
    

上面的解决方案是递归的,Ole V.V.如果输入数字很大并且堆栈内存分配相对较少,则this answer 关于可能的***Error 有一个好处。基于他对迭代解决方案需要的建议,我提供以下迭代方法的要点:

static boolean solve(int x, int y) 
    while (x > 0 && y > 0) 
        if (x == y) 
            return (x == 1); // x and y can be same if they are 1, otherwise impossible
        
        if (x > y) 
            x = x - y;
         else 
            y = y - x;
        
    
    return false;

【讨论】:

感谢病毒 Lalakia。但是代码仍然超出了提供的内存限制。还有比这更有效的方法吗? @KeerthiNandigam,我只纠正了暴力解决方案中的逻辑错误。如果对于给定的内存限制和给定的大量输入值,这种蛮力解决方案达不到标准,则需要一种优化的方法。我建议使用有关内存限制和输入范围的所有信息来编辑您的问题。 @KeerthiNandigam 您需要支持多大的输入数字?在我的 Java 上(我认为使用标准设置),您的程序在数字高达 9000 时运行良好(但如果它们是 10 000 则崩溃)。 @KeerthiNandigam,我已经用优化的解决方案之一编辑了我的答案,并附有解释,这对于我对(1, 1):true; (1, 1000000):true; (1000000, 1):true; (10000, 1000000):false; (1000000, 10000):false; (196418, 317811):true; (317811, 196418):true 的测试来说效果很好。我已经提供了要点,但您将获得完整的解决方案。【参考方案2】:

优化迭代方案

但是,代码仍然无法正常工作。它给了我内存限制 超出错误。这个程序还有其他逻辑吗?

在我的 Java 上,如果任一输入为 10 000 或更多,您的程序与 Viral Lalakia 的答案中的修复程序会因堆栈溢出而崩溃。可以通过调整 JVM 的内存分配来调整限制,但实际上您无法将内存增加到您可能喜欢的任何值。有效的解决方案是避免递归并编写迭代解决方案。

我的主要想法是从目标 (x, y) 向后迭代。 Viral Lalakia 的优化方案也是从目的地倒退,节省了时间,但是只要使用递归,我怀疑它并没有节省空间,这就是递归程序用完了。

所以在一个循环中从 x 和 y 中找到之前的坐标。

如果 x 如果相反 x > y 则做相反的减法。 如果 x == y 则没有可能的先前坐标。如果我们在起点 (1, 1),我们知道存在解决方案。返回真。如果不是,则返回 false。

每次循环检查你减少的数字是否小于1。如果小于1,则无法解决。返回 false。

顺便说一句,该算法与求 x 和 y 的最大公约数的算法相同。所以最简单的解决方案是BigInteger.valueOf(x).gcd(BigInteger.valueOf(y)) 并将结果与​​BigInteger.ONE 进行比较以确定路径是否存在。

链接: Answer by Viral Lalakia containing an optimized recursive solution

【讨论】:

【参考方案3】:

Viral Lalakia已经指出了你遇到的逻辑问题,但是目前的方法浪费了很多内存。

说明:您正在使用递归,这是对 stack 的隐式使用,您调用的方法及其状态存储在其中。如果 x 和 y 是非常大的数字,那么您将遇到堆栈溢出问题(不是双关语!)。

解决方案

我们知道

最初你在点 (1, 1) 您的 (a, b) 位置是 (1, 1)、(a' + b, b) 或 (a, a + b') 如果a > b,那么a = a' + b,所以,前一个位置是(a - b, b) 如果a 如果 a == b,那么您要么处于 (1, 1) 的初始位置,要么无法通过绝对值确定之前的状态

这允许您节省大量内存,假设您以迭代方式实现它。您需要遵守的想法如下:

如果达到 (x, y),那么算法结束,不管发生什么,因为问题是它是否可解,你不需要存储实际的解 当您执行一个步骤时,您需要知道您是否将其中一个坐标添加到另一个坐标中,或者您是否已经后退了一步 如果该点看起来像(a,a),那么您需要将之前的值存储在堆栈中,以及您所采取的方向,这样您就可以回到它,(a, a) 必须以特殊方式处理 如果你不只是在向后移动之后,那么首先尝试增加左坐标 如果您刚刚在向后移动之后并且在向后移动之前,a > b 为真,或者 a == b 为真,条件是在我之前提到的堆栈中方向在左侧,然后增加第二个坐标而不是第一个坐标并更改堆栈顶部条目的方向(如果存在) 如果您刚刚向后移动并且 a 如果您执行向后移动并且向后移动之前的位置类似于 (a, a),那么您可以从堆栈中删除相应的条目 如果在向后移动后到达 (1, 1),那么无论方向如何,您都可以结束算法,因为步长 (2, 1) 与步长 (1) 相同, 2),包括子树,由于加法的交换性质

【讨论】:

另外,你可以增加内存限制。【参考方案4】:

看看:

if(sol(a, a+b, x, y)) 返回假;

那个条件应该返回真,正如问题“你只能移动到 (a+b,b) 或 (a,a+b)”中提到的那样。

import java.util.*;
class Solution
    static boolean sol(int a, int b, int x, int y)
        if( a == x && b == y)
            return true;
        else if(a > x || b > y)
            return false;
        else
            if(sol(a+b, a, x, y)) return true;
            if(sol(a, a+b, x, y)) return true;
        
        return false;
    
    static String ans(int x, int y)
        if(sol(1, 1, x, y)) return "Yes";
        return "No";
    
    public static void main(String[] args) 
        int x,y;
        Scanner sc = new Scanner(System.in);
            x = sc.nextInt();
            y = sc.nextInt();
            System.out.println(ans(x, y));
        
    
    

最佳方法:

使用二维矩阵 (x * y) 来了解该单元格是否被访问以及是否被访问来存储结果。

未访问 -> -1

已访问并可能从该位置到达 (x,y) -> 1

从该位置访问并可能到达 (x,y) -> 0

import java.util.*;
class Solution
    
    static boolean sol(int a, int b, int x, int y, int[][] dp)
        if( a == x && b == y)
            return true;
        else if(a > x || b > y)
            return false;
        else if(dp[a][b] == 1)
            return true;
        else if(dp[a][b] == 0)
            return false;
        else if(sol(a+b, a, x, y, dp) || sol(a, a+b, x, y, dp))
                dp[a][b] = 1;
                return true;
        
        dp[a][b] = 0;
        return false;
    
    
    static String ans(int x, int y, int[][] dp)
        if(sol(1, 1, x, y, dp)) return "Yes";
        return "No";
    
    
    public static void main(String[] args) 
        int x,y;
        int[][] dp;
        
        Scanner sc = new Scanner(System.in);
        x = sc.nextInt();
        y = sc.nextInt();
        
        dp = new int[x+1][y+1];
        for(int[] row : dp)
            Arrays.fill(row,-1);
        
        System.out.println(ans(x, y, dp));
        
    
    

【讨论】:

谢谢@Yash Shah...但是,代码仍然无法正常工作。它给了我超出内存限制的错误。这个程序还有其他逻辑吗? @KeerthiNandigam 你能分享一个问题的链接吗? 作为评估的一部分,有人问我这个问题。现在关门了。但我认为你可以在 HackerEarth 中找到它。感谢您的解决方案?

以上是关于假设初始位置为 (1, 1),请在每一步通过以下给定规则找出是不是可以到达 (x, y) [关闭]的主要内容,如果未能解决你的问题,请参考以下文章

739. 每日温度

为啥这棵决策树在每一步的值不等于样本数?

单调栈——每日温度

单调栈——每日温度

Ramdom Walk with Restart

算法系列——每日温度