Assuming initial position is (1, 1), find out if you can reach (x, y) by the following given rules at each step

huangapple go评论82阅读模式
英文:

Assuming initial position is (1, 1), find out if you can reach (x, y) by the following given rules at each step

问题

Sure, here is the translated code:

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, y;
        Scanner sc = new Scanner(System.in);
        x = sc.nextInt();
        y = sc.nextInt();
        System.out.println(ans(x, y));
    }
}

Regarding efficiency and issues in your code, your approach is based on recursion, and while it works, it's not very efficient for larger values of x and y. The problem with your code is that it tries all possible combinations of moves to reach the destination (x, y), which leads to a high number of recursive calls and redundant calculations.

A more efficient approach is to use an iterative method like the Euclidean algorithm for finding the greatest common divisor (GCD). You can compute the GCD of x and y, and if it's equal to 1, then it's possible to reach (x, y) using the given moves. If the GCD is not 1, then it's not possible to reach (x, y).

Here's how you can modify your code using this approach:

import java.util.*;

class Solution {
    static int gcd(int a, int b) {
        if (b == 0)
            return a;
        return gcd(b, a % b);
    }

    static String ans(int x, int y) {
        return gcd(x, y) == 1 ? "Yes" : "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));
    }
}

This approach is more efficient and avoids the exponential time complexity of the recursive approach.

英文:

> You are given the coordinates (x,y). Initially, you are at (1,1) and
> are required to go to (x,y) using the following rule: If the current
> position is (a,b) then in the next move, you can move only to (a+b,b)
> or (a,a+b). Write a program to check if you can reach (x,y) using
> only the described moves.

I have tried to solve this problem that I mentioned above in Java through recursion. But the program was not working for some test cases. I could not find what is wrong. But I know it is a brute force method.

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));
		
	}
	
}

This is the code that I wrote. Can someone tell me an efficient for this solving this and tell me what's wrong with my code?

答案1

得分: 4

Your question says:

If the current position is (a, b) then in the next move, you can move only to (a+b, b) or (a, a+b).

Your mistakes are (highlighted inline in bold):

  1. <code>if(sol(a+b, **a**, x, y)) return true; // a should be b</code>
  2. <code>if(sol(a, a+b, x, y)) return **false**; // false should be true</code>

Correct both points with (highlighted inline in bold):

  1. <code>if(sol(a+b, **b**, x, y)) return true;</code>
  2. <code>if(sol(a, a+b, x, y)) return **true**;</code>

OR simplify by combining them:
<code>if(sol(a+b, **b**, x, y) || sol(a, a+b, x, y)) return true;</code>

With the above suggestion, the full code would be:

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, y;
        Scanner sc = new Scanner(System.in);
        x = sc.nextInt();
        y = sc.nextInt();
        System.out.println(ans(x, y));
    }
}

Edit for optimized solution than brute force

We are starting with (1, 1) and any next step of (a, b) is (a+b, b) or (a, a+b) so x > 0 and y > 0 is the required condition for any step (x, y).

Now, let's say,

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

So if we want to derive step#k from step#k+1 then we can say (a, b) can be one of these two: (a2 - b2, b2) OR (a2, b2 - a2). We can easily remove one option as we know that a > 0 and b > 0.

  • If (a2 - b2) > 0 then (a, b) must be (a2 - b2, b2).
  • Similarly, if (b2 - a2) > 0 then (a, b) must be (a2, b2 - a2).
  • If a2 == b2 (and not 1) then a2 - b2 and b2 - a2 will be 0 which is impossible as a > 0 and b > 0 is a required condition.

So, we will start from destination (x, y) and try to reach (1, 1) by the above observation in linear time. The gist will be:

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);
    }
}

The above solution is recursive and Ole V.V. has a good point in this answer about possible StackOverflowError if input numbers are large and stack memory allocation is relatively less. Based on his suggestion of the need for an iterative solution, I am providing a simplified version for an iterative approach:

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;
}
英文:

Your question says:
> If the current position is (a,b) then in the next move, you can move only to (a+b,b) or (a,a+b).

Your mistakes are (highlighted inline in bold):

  1. <code>if(sol(a+b, a, x, y)) return true; // a should be b</code>
  2. <code>if(sol(a, a+b, x, y)) return false; // false should be true</code>

Correct both points with (highlighted inline in bold):

  1. <code>if(sol(a+b, b, x, y)) return true;</code>
  2. <code>if(sol(a, a+b, x, y)) return true;</code>

OR simplify by combining them:
<code>if(sol(a+b, b, x, y) || sol(a, a+b, x, y)) return true;</code>

With above suggestion full code would be:

import java.util.*;
class Solution {
    static boolean sol(int a, int b, int x, int y) {
        if(a == x &amp;&amp; b == y)
            return true;
        else if(a &gt; x || b &gt; 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 &quot;Yes&quot;;
        return &quot;No&quot;;
    }
    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));
    }
}

Edit for optimized solution than brute force

We are starting with (1, 1) and any next step of (a, b) is (a+b, b) or (a, a+b) so x &gt; 0 and y &gt; 0 is the required condition for any step (x, y).

Now, let's say,

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

So if we want to derive step#k from step#k+1 then we can say (a, b) can be one of these two: (a2 - b2, b2) OR (a2, b2 - a2). We can easily remove one option as we know that a &gt; 0 and b &gt; 0.

  • If (a2 - b2) &gt; 0 then (a, b) must be (a2 - b2, b2).
  • Similarly, if (b2 - a2) &gt; 0 then (a, b) must be (a2, b2 - a2).
  • If a2 == b2 (and not 1) then a2 - b2 and b2 - a2 will be 0 which is impossible as a &gt; 0 and b &gt; 0 is required condition.

So, we will start from destination (x, y) and try to reach (1, 1) by above observation in linear time. The gist will be:

static boolean solve(int x, int y) {
    if (x == 1 &amp;&amp; y == 1) {
    	return true;
    }
    if (x == y || x &lt; 1 || y &lt; 1) {
    	return false;
    }
    if (x &gt; y) {
    	return solve(x - y, y);
    } else {
    	return solve(x, y - x);
    }
}

Above solution is recursive and Ole V.V. has a good point in this answer about possible StackOverflowError if input numbers are large and stack memory allocation is relatively less. Based on his suggestion of the need of iterative solution, I am providing below gist for iterative approach:

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

答案2

得分: 3

优化的迭代解决方案

> 但是,代码仍然无法运行。它给了我超出内存限制的错误。这个程序是否有任何其他的逻辑?

在我的 Java 中,您使用了 Viral Lalakia 答案中的修复后的代码,如果输入的任一部分是 10,000 或更多,程序会崩溃并导致堆栈溢出。可以通过调整 JVM 的内存分配来调整限制,但在实际情况下,您无法将内存增加到任何您想要的大小。有效的解决方案是避免递归,而是编写迭代的解决方案。

我的关键思想是从目标(x,y)开始向后迭代。Viral Lalakia 的优化解决方案也是从目标向后进行,这节省了 时间,但只要使用递归,我怀疑它并没有节省 空间,而递归程序正在耗尽空间。

因此,在循环中查找从 x 和 y 得到的先前坐标。

  • 如果 x < y,则先前坐标必须是(x,y - x)。因此从 y 减去 x。
  • 相反地,如果 x > y,则执行相反的减法操作。
  • 如果 x == y,则没有可能的先前坐标。如果我们位于起始点(1, 1),则知道存在解决方案。返回 true。如果不是,则返回 false。

每次通过循环时,检查您已减少的数字是否变为小于 1。如果是,就不可能有解决方案。返回 false。

顺便提一下,该算法与找到 x 和 y 的最大公约数的算法相同。因此,非常简单的解决方案是 BigInteger.valueOf(x).gcd(BigInteger.valueOf(y)),然后将结果与 BigInteger.ONE 进行比较,以确定是否存在路径。

链接: Viral Lalakia 提供的优化递归解决方案的答案

英文:

Optimized iterative solution

> but still, the code isn't working. It's giving me memory limit
> exceeded error. Is there any other logic for this program?

On my Java your program with the fixes from the answer from Viral Lalakia crashes with a stack overflow if either input is 10 000 or more. The limit may be adjusted by adjusting the memory allocation for your JVM, but in practice you cannot increase memory to whatever you might like. The effective solution is to avoid recursion and code an iterative solution.

My key idea is to iterate backward from the destination (x, y). Viral Lalakia’s optimized solution also goes backward from the destination, which saves time, but as long as recursion is used, I suspect that it doesn’t save space, which is what the recursive program is running out of.

So in a loop find the previous coordinates from x and y.

  • If x < y the previous coordinates must have been (x, y - x). So subtract x from y.
  • If conversely x > y do the opposite subtraction.
  • If x == y there are no possible previous coordinates. If we are at the starting point (1, 1), we know that a solution exists. Return true. If not, return false.

For each time through the loop check whether the number that you have decreased has become less than 1. If it has, there cannot be a solution. Return false.

BTW, the algorithm is the same as the algorithm for finding the greatest common divisor of x and y. So the very easy solution would be BigInteger.valueOf(x).gcd(BigInteger.valueOf(y)) and compare the result to BigInteger.ONE to determine whether a path exists.

Link: Answer by Viral Lalakia containing an optimized recursive solution

答案3

得分: 2

Viral Lalakia已经指出了你遇到的逻辑问题,然而,当前的方法浪费了大量的内存。

Explanation:你使用了递归,这是隐式使用了堆栈,其中存储了你调用的方法及其状态。如果x和y是非常大的数,那么你将会遇到堆栈溢出的问题(没有刻意的双关意思!)。

解决方案

我们知道:

  • 最初你在点(1, 1)上。
  • 你的位置(a, b)可以是(1, 1)、(a' + b, b)或者(a, a + b')。
  • 如果a > b,那么a = a' + b,所以,之前的位置是(a - b, b)。
  • 如果a < b,那么b = a + b',所以,之前的位置是(a, b - a)。
  • 如果a == b,那么你要么在初始位置(1, 1),要么仅凭数值无法确定前一个状态。

这使得你可以节省很多内存,假设你采用迭代来实现这一点。你需要遵循的思路如下:

  • 如果你达到了(x, y)这个点,算法就结束了,无论发生了什么,因为问题是是否可解,你不需要存储实际的解决方案。
  • 当你执行一步操作时,你需要知道是否将其中一个坐标添加到另一个坐标上,或者你已经后退了一步。
  • 如果点的形式是(a, a),那么你需要在堆栈中存储前一个值,以及你采取的方向,这样你就可以回到它,(a, a)的位置必须以特殊方式处理。
  • 如果你不是在刚刚进行了后退操作之后,那么首先尝试增加左坐标。
  • 如果你刚刚进行了后退操作,并且在采取后退操作之前,要么a > b为真,要么a == b为真,并且在我之前提到的堆栈中方向是向左的,那么就增加第二个坐标,而不是第一个坐标,并且如果存在堆栈的顶部条目,则更改其方向。
  • 如果你刚刚进行了后退操作,并且要么a < b为真,要么a == b为真,并且在堆栈中第一个坐标高于第二个坐标,则下一步将继续是后退操作。
  • 如果你执行了一步后退操作,并且在后退操作之前的位置是(a, a)这样的形式,那么你可以从堆栈中移除相应的条目。
  • 如果在后退操作后,你达到了(1, 1)这个位置,那么你可以结束算法,无论方向如何,因为步骤(2, 1)与步骤(1, 2)是相同的,包括子树,这是加法的交换性质所导致的。
英文:

Viral Lalakia has already pointed out the logical problems you had, however, the current approach wastes a lot of memory.

Explanation: You are using recursion, which is implicit usage of the stack, where your called methods and their status is stored. If x and y are VERY large numbers, then you will get a stack overflow issue (no pun intended!).

Solution

We know that

  • initially you are on point (1, 1)
  • your (a, b) position is either (1, 1), (a' + b, b) or (a, a + b')
  • if a > b, then a = a' + b, so, the previous position is (a - b, b)
  • if a < b, then b = a + b', so, the previous position is (a, b - a)
  • if a == b, then you are either on the initial position of (1, 1), or, you cannot determine by the sheer values what was the previous state

This allows you to save a LOT of memory, assuming that you implement this iteratively. The ideas you need to comply to are as follows:

  • if you have reached (x, y), then the algorithm ends, no matter what happened, as the question is whether it's solvable, you do not need to store the actual solution

  • when you perform a step, you need to know whether you have added one of the coordinates to the other, or you have taken a step back

  • if the point looks like (a, a), then you need to store what the previous value was in a stack, as well as the direction you have taken, so you can go back to it, positions of (a, a) must be treated in a special manner

  • if you are NOT just after a backwards move, then try to increase the left coordinate first

  • if you are just after a backwards move and before taking a backwards move, either a > b was true, or a == b was true with the condition that in the stack I have mentioned earlier the direction was in the left, then increment the second coordinate instead of the first one and change the direction of the top entry of the stack if it exists

  • if you are just after a backwards move and either a < b was true, or a == b was true with the condition that in the stack the first coordinate is higher than the second, then the next move will be backwards as well

  • if you perform a backwards move and the position before the backwards move was like (a, a), then you can remove the corresponding entry from your stack

  • if, after a backward move you reach (1, 1), then you can end the algorithm, regardless of what the direction was, because having a step of (2, 1) is identical with having the step of (1, 2), including the subtrees, due to the commutative nature of adding

答案4

得分: 1

看一下:

> if(sol(a, a+b, x, y)) return false;

该条件应该返回true,正如问题中所提到的“您只能移动到(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));
        
    }
    
}
英文:

Take a look at:

> if(sol(a, a+b, x, y)) return false;

That condition should return true as mentioned in the problem that "you can move only to (a+b,b) or (a,a+b)".

import java.util.*;
class Solution{
    static boolean sol(int a, int b, int x, int y){
        if( a == x &amp;&amp; b == y)
            return true;
        else if(a &gt; x || b &gt; 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 &quot;Yes&quot;;
        return &quot;No&quot;;
    }
    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));
        
    }
    
}

OPTIMAL APPROACH:

Use a two-dimensional matrix (x * y) to know whether that cell is visited or not and if visited to store the result.

Not visited -> -1

visited and possible to reach to (x,y) from that position -> 1

visited and possible to reach to (x,y) from that position -> 0

import java.util.*;
class Solution{
    
    static boolean sol(int a, int b, int x, int y, int[][] dp){
        if( a == x &amp;&amp; b == y)
            return true;
        else if(a &gt; x || b &gt; 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 &quot;Yes&quot;;
        return &quot;No&quot;;
    }
    
    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));
        
    }
    
}

huangapple
  • 本文由 发表于 2020年9月19日 16:08:10
  • 转载请务必保留本文链接:https://go.coder-hub.com/63966620.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定