Two Bucket

1. Readme

两个桶

给定两个不同尺寸的桶,演示如何通过两个桶,策略性地传输液体来测量精确的升数.

由于这个数学问题,很容易受到解释/个体方法的影响,因此这些测试代码专门针对期望一个总体解决方案而编写.

为了提供帮助,测试代码首先为桶(1/2)装满。这意味着,若开始时,选择较大的桶装满,那就不会出现,较小的桶装满而较大的桶为空的情况。(也就是说,起点应该用较小桶);不然这会破坏比较两种方法的目的!

您的程序将作为输入:

  • 桶 1 的大小
  • 桶 2 的大小
  • 要达到的理想升数
  • 首先要装满哪个桶,要么是桶 1 还是桶 2

您的计划应确定:

  • 达到所需的升数,所需的”移动”总数,包括第一次装满
  • 哪个桶应该以所需的升数结束(假设这是桶 A) - 要么桶 1 ,要么桶 2
  • 另一个桶中,剩下多少升(桶 B)

注意:任何时候对其中一个或两个桶进行更改,都计为一(1)次移动。

示例:桶 1 最多可容纳 7 升,桶 2 最多可容纳 11 升。让我们说桶 1,第一步,持有 7 升,桶 2 持有 8 升(7,8)。如果您清空桶 1,并且不对桶 2 进行任何更改,则分别为 0 升和 8 升(0,8),这将作为一个”移动”。相反,如果你已经把桶 1 倒入桶 2,直到桶 2 充满,那留下的,桶 1 中的 4 升和桶 2 中的 11 升(4,11),这也算作只有一个”移动”。

总而言之,唯一有效的行动是:

  • 从一个桶,倒到另一个桶
  • 清空一个桶,对另一个什么都不做
  • 装满一个桶,对另一个什么也不做

写<3 于全栈教程 Academy作 者:Lindsay Levine.

资源

水浇注问题http://demonstrations.wolfram.com/WaterPouringProblem/

2. 开始你的表演

#[derive(PartialEq, Eq, Debug)]
pub enum Bucket {
   One,
   Two,
}

/// A struct to hold your results in.
#[derive(PartialEq, Eq, Debug)]
pub struct BucketStats {
   /// The total number of "moves" it should take to reach the desired number of liters, including
   /// the first fill.
   pub moves: u8,
   /// Which bucket should end up with the desired number of liters? (Either "one" or "two")
   pub goal_bucket: Bucket,
   /// How many liters are left in the other bucket?
   pub other_bucket: u8,
}

/// Solve the bucket problem
pub fn solve(capacity_1: u8, capacity_2: u8, goal: u8, start_bucket: &Bucket) -> BucketStats {
   unimplemented!(
       "Given one bucket of capacity {}, another of capacity {}, starting with {:?}, find pours to reach {}",
       capacity_1,
       capacity_2,
       start_bucket,
       goal,
   );
}

3. 测试代码查看


# #![allow(unused_variables)]
#fn main() {
#[test]
fn test_case_1() {
   assert_eq!(
       solve(3, 5, 1, &Bucket::One),
       BucketStats {
           moves: 4,
           goal_bucket: Bucket::One,
           other_bucket: 5,
       }
   );
}

#[test]
//#[ignore]
fn test_case_2() {
   assert_eq!(
       solve(3, 5, 1, &Bucket::Two),
       BucketStats {
           moves: 8,
           goal_bucket: Bucket::Two,
           other_bucket: 3,
       }
   );
}

#[test]
//#[ignore]
fn test_case_3() {
   assert_eq!(
       solve(7, 11, 2, &Bucket::One),
       BucketStats {
           moves: 14,
           goal_bucket: Bucket::One,
           other_bucket: 11,
       }
   );
}

#[test]
//#[ignore]
fn test_case_4() {
   assert_eq!(
       solve(7, 11, 2, &Bucket::Two),
       BucketStats {
           moves: 18,
           goal_bucket: Bucket::Two,
           other_bucket: 7,
       }
   );
}

#[test]
//#[ignore]
fn test_case_5() {
   assert_eq!(
       solve(1, 3, 3, &Bucket::Two),
       BucketStats {
           moves: 1,
           goal_bucket: Bucket::Two,
           other_bucket: 0,
       }
   );
}

#[test]
//#[ignore]
fn test_case_6() {
   assert_eq!(
       solve(2, 3, 3, &Bucket::One),
       BucketStats {
           moves: 2,
           goal_bucket: Bucket::Two,
           other_bucket: 2,
       }
   );
}

#}

4. 答案


# #![allow(unused_variables)]
#fn main() {
use std::collections::{HashSet, VecDeque};

// We can turn this problem into a simple graph searching problem. Each move represents an
// edge in our graph; the buckets' fill states form the graph's vertices. For example, say bucket
// one holds up to 7 liters, and bucket two holds up to 11 liters, and at the current step, bucket
// one has 7 liters and bucket two has 8 liters: (7, 8). By emptying the first bucket, we form an
// edge (7, 8) -> (0, 8). Similarly, by pouring the first bucket into the second, we form an edge
// (7, 8) -> (4, 11).
//
// Since we want to minimize the number of moves, we search the graph breadth-first, starting with
// the configuration provided as the problem input. Note that, to avoid cheating, we mark both
// possible starting configurations as visited; otherwise, our search might just empty the initial
// bucket and fill the other one.

#[derive(PartialEq, Eq, Debug)]
pub enum Bucket {
   One,
   Two,
}

/// A struct to hold your results in.
#[derive(PartialEq, Eq, Debug)]
pub struct BucketStats {
   /// The total number of "moves" it should take to reach the desired number of liters, including
   /// the first fill.
   pub moves: u8,
   /// Which bucket should end up with the desired number of liters? (Either "one" or "two")
   pub goal_bucket: Bucket,
   /// How many liters are left in the other bucket?
   pub other_bucket: u8,
}

/// Solve the bucket problem
pub fn solve(capacity_1: u8, capacity_2: u8, goal: u8, start_bucket: &Bucket) -> BucketStats {
   let state = match *start_bucket {
       Bucket::One => (capacity_1, 0),
       Bucket::Two => (0, capacity_2),
   };

   let mut next_search = VecDeque::new();
   let mut visited = HashSet::new();
   let mut moves = 1;

   next_search.push_front(state);

   // "Visit" both starting states. This will ensure that we don't cheat, i.e.
   // empty our starting bucket completely and fill the other bucket.
   visited.insert((capacity_1, 0));
   visited.insert((0, capacity_2));

   loop {
       let mut current_search = next_search;
       next_search = VecDeque::new();

       for state in current_search.drain(0..) {
           let (bucket_1, bucket_2) = state;

           if bucket_1 == goal {
               return BucketStats {
                   moves: moves,
                   goal_bucket: Bucket::One,
                   other_bucket: bucket_2,
               };
           } else if bucket_2 == goal {
               return BucketStats {
                   moves: moves,
                   goal_bucket: Bucket::Two,
                   other_bucket: bucket_1,
               };
           }

           // Empty the first bucket
           let empty_1 = (0, bucket_2);
           if !visited.contains(&empty_1) {
               next_search.push_front(empty_1);
               visited.insert(empty_1);
           }

           // Empty the second bucket
           let empty_2 = (bucket_1, 0);
           if !visited.contains(&empty_2) {
               next_search.push_front(empty_2);
               visited.insert(empty_2);
           }

           // Fill the first bucket
           let fill_1 = (capacity_1, bucket_2);
           if !visited.contains(&fill_1) {
               next_search.push_front(fill_1);
               visited.insert(fill_1);
           }

           // Fill the second bucket
           let fill_2 = (bucket_1, capacity_2);
           if !visited.contains(&fill_2) {
               next_search.push_front(fill_2);
               visited.insert(fill_2);
           }

           // Pour the first bucket into the second bucket
           let pour_1_into_2 = if bucket_1 + bucket_2 <= capacity_1 {
               (bucket_1 + bucket_2, 0)
           } else {
               (capacity_1, bucket_1 + bucket_2 - capacity_1)
           };
           if !visited.contains(&pour_1_into_2) {
               next_search.push_front(pour_1_into_2);
               visited.insert(pour_1_into_2);
           }

           // Pour the second bucket into the first bucket
           let pour_2_into_1 = if bucket_1 + bucket_2 <= capacity_2 {
               (0, bucket_1 + bucket_2)
           } else {
               (bucket_1 + bucket_2 - capacity_2, capacity_2)
           };
           if !visited.contains(&pour_2_into_1) {
               next_search.push_front(pour_2_into_1);
               visited.insert(pour_2_into_1);
           }
       }

       moves += 1;
   }
}

#}



填充/相关