浇水:图形实现问题-最短路径/ Dijkstra(?)

时间:2018-12-06 02:34:13

标签: c++ algorithm graph-theory

编辑:重新发布;尝试了更多的问题,然后重新发布。此时我真的迷路了。

今天要解决这个小图形问题,想知道是否有人对此有任何解决方案/见解。

给出两个容器,其中一个容器可以容纳一升水,另一个容器可以容纳一升水,确定要在其中一个容器中获得准确目标水量所需的步骤数,如果不能,则为-1。完成。

开始时,两个容器都是空的。以下操作被视为“步骤”:

  • 清空容器
  • 填充容器
  • 将一个容器中的水倒入另一个容器中,不要溢出,直到其中一个容器装满或倒空

为了让您入门,我们为您提供了一个Node和一个Graph类。您的工作是实现函数Graph createGraph(int Capacity_a,int Capacity_b),该函数将构建一个包含给定两个容器容量的所有可能容器状态的图形,以及一个int findSolution(Graph g,int target),该函数将图形遍历并返回最小步长。

您可以根据需要修改结构,也可以在不使用提供的结构的情况下构建自己的解决方案。我们只会测试您的int waterPouring(int a,int b,int target)函数。

提示:哪种算法可以保证达到目标的最少步骤?

我最初的猜测是Dijkstra的findSolution(),并试图翻译一些伪代码,但没有成功。我(认为)我正确实现了createGraph。不确定要去哪里/是否有更好的方法可以做到这一点。这是代码:

谢谢!

waterPouring.cpp:

gallows = [
r'''
---------
|       |
|       |
|
|
|
|
|
|
|
|
---------------
''',
r'''
---------
|       |
|       |
|       O
|
|
|
|
|
|
|
---------------
''',
r'''
---------
|       |
|       |
|       O
|       |
|       |
|      
|
|
|
|
---------------
''',
r'''
---------
|       |
|       |
|       O
|     __|
|       |
|      
|
|
|
|
---------------
''',
r'''
---------
|       |
|       |
|       O
|     __|__
|       |
|      
|
|
|
|
---------------
''',
r'''
---------
|       |
|       |
|       O
|     __|__
|       |
|      /
|     /
|
|
|
---------------
''',
r'''
---------
|       |
|       O
|     __|__
|       |
|      / \
|     /   \
|
|
|
|
---------------
''',
r'''
---------
|       |
|       X
|     __|__
|       |
|      / \
|     /   \
|
|
|
---------------
''']

counter = 0

for i in range(len(gallows)):
    print(gallows[counter])
    counter += 1

1 个答案:

答案 0 :(得分:3)

如果您不使用图形就可以使用解决方案(如果任务描述允许的话),您可以执行以下操作:

假设您有两个容量分别为a和b的容器,最后需要获取c升。

假设您有两个容量分别为ab的容器,并且最终需要获取c升。

首先观察到,您执行的每项操作都可以移动x * a + y * b升水。例如。如果您要从第二个容器倒满第一个容器,则您要倒1 * b - 1 * a。您可以继续说服自己这是真的。这给了我们下面的等式:

x * a + y * b = c

这是一个双色子方程,如果gcd(a, b)除以c(请参阅Bézout's identity),则有一个解。您可以使用extended Euclidean algorithm解决它。如果c小于max(a, b),则xy小于零。假设x > 0。然后,您需要将a个容器x装满,将水倒入b个容器y次。

示例:a = 9b = 5c = 6。我们有

-1 * 9 + 3 * 5 = 6

所以,我们需要

0 5 // Full the second (1)
5 0 // Pour to the first
5 5 // Full the second (2)
9 1 // Pour to the first
0 1 // Empty the first (-1)
1 0 // Pour to the first
1 5 // Full the second (3)
6 0 // Pour to the first

但是如果您真的想使用图形,那么

#include <iostream>
#include <algorithm>
#include <numeric>
#include <vector>
#include <queue>

struct Node { int a, b; };

class Graph {
public:
    std::vector<std::pair<Node, std::vector<int>>> nodes;

    static Graph Create(int a, int b) {
        auto index = [a,b](int i, int j) {
            return i * (b + 1) + j;
        };

        Graph g;
        for (int i = 0; i <= a; ++i) {
            for (int j = 0; j <= b; ++j) {
                std::vector<int> adj;
                if (i < a) adj.push_back(index(a, j));
                if (i > 0) adj.push_back(index(0, j));
                if (j < b) adj.push_back(index(i, b));
                if (j > 0) adj.push_back(index(i, 0));
                int da = std::min(a - i, j);
                int db = std::min(b - j, i);
                adj.push_back(index(i + da, j - da));
                adj.push_back(index(i - db, j + db));
                std::sort(adj.begin(), adj.end());
                adj.erase(std::unique(adj.begin(), adj.end()), adj.end());
                g.nodes.push_back({ { i,j }, adj });
            }
        }
        return g;
    }

    // Breadth-first search
    std::pair<int, std::vector<int>> Shortest(int target) const {
        std::vector<bool> visited(nodes.size(), 0);
        std::vector<int> dist(nodes.size(), std::numeric_limits<int>::max());
        std::vector<int> prev(nodes.size(), -1);
        std::queue<int> q;
        int cur_dist = 0;
        q.push(0); visited[0] = true; dist[0] = 0;
        while (q.size() > 0) {
            int index = q.front(); q.pop();
            for (auto i : nodes[index].second) {
                if (nodes[i].first.a == target || nodes[i].first.b == target) {
                    int j = index;
                    std::vector<int> path = { i, index };
                    while (prev[j] != -1) {
                        path.push_back(j = prev[j]);
                    }                    
                    return { dist[index] + 1, path };
                }
                if (!visited[i]) {
                    q.push(i); visited[i] = true; dist[i] = dist[index] + 1; prev[i] = index;
                }
            }
        }
        return { -1, {} };
    }
};

int main()
{
    const auto g = Graph::Create(9, 5);
    const auto p = g.Shortest(6);
    for (int i = (int)p.second.size() - 1; i >= 0; --i) {
        std::cout << g.nodes[p.second[i]].first.a << " " << g.nodes[p.second[i]].first.b << std::endl;
    }
    std::cout << std::endl << p.first << std::endl;
    return 0;
}

输出相同:

  

0 0
  0 5
  5 0
  5 5
  9 1
  0 1
  1 0
  1 5
  6 0

     

8