📜  两个水壶拼图

📅  最后修改于: 2021-05-06 09:49:54             🧑  作者: Mango

你在河边。给您一个m升的水罐和一个n升的水罐,其中0 。两个水罐最初都是空的。水壶没有标记,可以测量更少量。您必须使用水壶来测量d 您可以执行的操作是:

  1. 倒空一壶
  2. 装满水罐
  3. 将水从一个水罐倒入另一个水罐,直到其中一个水罐倒空或装满。

解决此问题的方法有多种,包括BFS和DP。在本文中,讨论了一种解决该问题的算术方法。可以通过形式为mx + ny = d的Diophantine方程对问题进行建模,当且仅当gcd(m,n)除以d时,该方程才可解决。同样,可以使用用于GCD的扩展Euclid算法来给出满足方程式的解x,y。
例如,如果我们有一个5升的水罐J1(n = 5)和另一个3升的水罐J2(m = 3),我们必须使用它们来量取1升的水。关联的等式为5n + 3m =1。首先,由于gcd(3,5)= 1除以1(因此请参见详细说明),因此可以解决此问题。使用扩展Euclid算法,我们得到满足方程的n和m值,即n = 2和m = -3。 n,m的这些值也具有一些含义,例如此处n = 2和m = -3意味着我们必须两次填充J1且将J2空三倍。
现在,要找到要执行的最少操作数,我们必须确定应该先装满哪个水罐。根据选择要填充的水罐还是要清空的水罐,我们有两种不同的解决方案,而其中的最小解决方案就是我们的答案。

解决方案1(始终从m升水罐倒入n升水罐)

  1. 装满m升的水罐,然后倒入n升的水罐中。
  2. 每当m升的水罐变空时,就将其装满。
  3. 每当n升水罐变满时,它就会倒空。
  4. 重复步骤1,2,3,直到n升水罐或m升水罐中装有d升水为止。

第1、2和3步的每一步都算作我们执行的一项操作。假设算法1无需操作即可实现C1中的任务。

解决方案2(始终从n升水罐倒入m升水罐)

  1. 装满n升水罐并将其倒入m升水罐。
  2. 每当n升水罐变空时,将其装满。
  3. 每当m升的水罐变满时,它就会倒空。
  4. 重复步骤1、2和3,直到n升水罐或m升水罐中装有d升水为止。

让我们说解决方案2无需操作即可实现C2中的任务。
现在,我们最终的解决方案将是C1和C2的最小值。
现在,我们说明两种解决方案的工作方式。假设有一个3升的水罐和一个5升的水罐来测量4升水,因此m = 3,n = 5且d = 4 。关联的丢番莲方程将为3m + 5n =4。我们配对(x,y)来表示每个浇筑步骤中3升水罐和5升水罐内的水量。

使用解决方案1,连续的浇注步骤是:

(0,0)->(3,0)->(0,3)->(3,3)->(1,5)->(1,0)->(0,1)->(3,1)->(0,4)

因此,您需要执行的操作数为8。

使用解决方案2,连续的浇注步骤是:

(0,0)->(0,5)->(3,2)->(0,2)->(2,0)->(2,5)->(3,4)

因此,您需要执行的操作数为6。
因此,我们将使用解决方案2在6次操作或移动中测量4升水。

基于此处的解释是实现。

C++
// C++ program to count minimum number of steps
// required to measure d litres water using jugs
// of m liters and n liters capacity.
#include 
using namespace std;
 
// Utility function to return GCD of 'a'
// and 'b'.
int gcd(int a, int b)
{
    if (b==0)
       return a;
    return gcd(b, a%b);
}
 
/* fromCap -- Capacity of jug from which
              water is poured
   toCap   -- Capacity of jug to which
              water is poured
   d       -- Amount to be measured */
int pour(int fromCap, int toCap, int d)
{
    // Initialize current amount of water
    // in source and destination jugs
    int from = fromCap;
    int to = 0;
 
    // Initialize count of steps required
    int step = 1; // Needed to fill "from" Jug
 
    // Break the loop when either of the two
    // jugs has d litre water
    while (from != d && to != d)
    {
        // Find the maximum amount that can be
        // poured
        int temp = min(from, toCap - to);
 
        // Pour "temp" liters from "from" to "to"
        to   += temp;
        from -= temp;
 
        // Increment count of steps
        step++;
 
        if (from == d || to == d)
            break;
 
        // If first jug becomes empty, fill it
        if (from == 0)
        {
            from = fromCap;
            step++;
        }
 
        // If second jug becomes full, empty it
        if (to == toCap)
        {
            to = 0;
            step++;
        }
    }
    return step;
}
 
// Returns count of minimum steps needed to
// measure d liter
int minSteps(int m, int n, int d)
{
    // To make sure that m is smaller than n
    if (m > n)
        swap(m, n);
 
    // For d > n we cant measure the water
    // using the jugs
    if (d > n)
        return -1;
 
    // If gcd of n and m does not divide d
    // then solution is not possible
    if ((d % gcd(n,m)) != 0)
        return -1;
 
    // Return minimum two cases:
    // a) Water of n liter jug is poured into
    //    m liter jug
    // b) Vice versa of "a"
    return min(pour(n,m,d),   // n to m
               pour(m,n,d));  // m to n
}
 
// Driver code to test above
int main()
{
    int n = 3, m = 5, d = 4;
 
    printf("Minimum number of steps required is %d",
           minSteps(m, n, d));
 
    return 0;
}


Java
// Java program to count minimum number of
// steps required to measure d litres water
// using jugs of m liters and n liters capacity.
import java.io.*;
 
class GFG{
 
// Utility function to return GCD of 'a'
// and 'b'.
public static int gcd(int a, int b)
{
    if (b == 0)
        return a;
         
    return gcd(b, a % b);
}
 
/* fromCap -- Capacity of jug from which
              water is poured
   toCap   -- Capacity of jug to which
              water is poured
   d       -- Amount to be measured */
public static int pour(int fromCap, int toCap,
                       int d)
{
     
    // Initialize current amount of water
    // in source and destination jugs
    int from = fromCap;
    int to = 0;
 
    // Initialize count of steps required
    int step = 1; // Needed to fill "from" Jug
 
    // Break the loop when either of the two
    // jugs has d litre water
    while (from != d && to != d)
    {
         
        // Find the maximum amount that can be
        // poured
        int temp = Math.min(from, toCap - to);
 
        // Pour "temp" liters from "from" to "to"
        to += temp;
        from -= temp;
 
        // Increment count of steps
        step++;
 
        if (from == d || to == d)
            break;
 
        // If first jug becomes empty, fill it
        if (from == 0)
        {
            from = fromCap;
            step++;
        }
 
        // If second jug becomes full, empty it
        if (to == toCap)
        {
            to = 0;
            step++;
        }
    }
    return step;
}
 
// Returns count of minimum steps needed to
// measure d liter
public static int minSteps(int m, int n, int d)
{
     
    // To make sure that m is smaller than n
    if (m > n)
    {
        int t = m;
        m = n;
        n = t;
    }
 
    // For d > n we cant measure the water
    // using the jugs
    if (d > n)
        return -1;
 
    // If gcd of n and m does not divide d
    // then solution is not possible
    if ((d % gcd(n, m)) != 0)
        return -1;
 
    // Return minimum two cases:
    // a) Water of n liter jug is poured into
    //    m liter jug
    // b) Vice versa of "a"
    return Math.min(pour(n, m, d), // n to m
                    pour(m, n, d)); // m to n
}
 
// Driver code
public static void main(String[] args)
{
    int n = 3, m = 5, d = 4;
 
    System.out.println("Minimum number of " +
                       "steps required is " +
                       minSteps(m, n, d));
}
}
 
// This code is contributed by RohitOberoi


Python3
# Python3 implementation of program to count
# minimum number of steps required to measure
# d litre water using jugs of m liters and n
# liters capacity.
def gcd(a, b):
    if b==0:
        return a
    return gcd(b, a%b)
 
 
''' fromCap -- Capacity of jug from which
              water is poured
   toCap   -- Capacity of jug to which
              water is poured
   d       -- Amount to be measured '''
def Pour(toJugCap, fromJugCap, d):
 
 
    # Initialize current amount of water
    # in source and destination jugs
    fromJug = fromJugCap
    toJug  = 0
 
    # Initialize steps required
    step = 1
    while ((fromJug  is not d) and (toJug is not d)):
 
         
        # Find the maximum amount that can be
        # poured
        temp = min(fromJug, toJugCap-toJug)
 
        # Pour 'temp' liter from 'fromJug' to 'toJug'
        toJug = toJug + temp
        fromJug = fromJug - temp
 
        step =  step + 1
        if ((fromJug == d) or (toJug == d)):
            break
 
        # If first jug becomes empty, fill it
        if fromJug == 0:
            fromJug = fromJugCap
            step =  step + 1
 
        # If second jug becomes full, empty it
        if toJug == toJugCap:
            toJug = 0
            step =  step + 1
             
    return step
 
# Returns count of minimum steps needed to
# measure d liter
def minSteps(n, m, d):
    if m> n:
        temp = m
        m = n
        n = temp
         
    if (d%(gcd(n,m)) is not 0):
        return -1
     
    # Return minimum two cases:
    # a) Water of n liter jug is poured into
    #    m liter jug
    return(min(Pour(n,m,d), Pour(m,n,d)))
 
# Driver code
if __name__ == '__main__':
 
    n = 3
    m = 5
    d = 4
 
    print('Minimum number of steps required is',
                              minSteps(n, m, d))
     
# This code is contributed by Sanket Badhe


输出:

Minimum number of steps required is 6

另一个详细说明:http://web.mit.edu/neboat/Public/6.042/numbertheory1.pdf