为什么这个Dijkstra算法不能用于这个特定的输入?

时间:2017-09-30 21:06:51

标签: c++ algorithm graph dijkstra

我已实施Dijkstra's algorithm。 但是当输入如下时它不起作用:

1
6 7
1 2 5
2 3 2
3 4 3
1 4 9
4 5 2
5 6 3
1 6 2
1

我在调试模式下运行它,以了解错误。似乎节点5没有插入单元中。我无法找出原因。

这里是代码:

#include<iostream>
#include<vector>
#include<list>
#include <limits>
#include <algorithm>
#include <queue>
#include <set>
#include <stack>
using namespace std;

struct compare
{
    bool operator()(pair<int, int> a, pair<int, int> b) const
    {
        return a.second < b.second;
    }

};

void printResults(vector<int> vec, int starting)
{
    for (int i = 1; i < vec.size(); i++)
    {
        if (vec[i] == numeric_limits<int>::max() && i != starting)
        {
            cout << -1 << " ";
        }
        else if (i != starting)
        {
            cout << vec[i] << " ";
        }
    }

}
void djkstra(vector<vector<pair<int, int>>>&vec, int starting, int number_of_vertices)
{
    int max = numeric_limits<int>::max();
    set <pair<int, int>,compare> queue;
    vector<bool> visited(number_of_vertices + 1, false);
    vector<int> distances(number_of_vertices + 1, max);
    vector<int> parents(number_of_vertices + 1, -1);
    queue.insert(make_pair(starting, 0));
    distances[starting] = 0;
    for (int i = 0; i<number_of_vertices-1; i++)
    {
        pair<int, int> minElem = *queue.begin(); //take min elem
        queue.erase(minElem);
        vector<pair<int, int>> adjacents = vec[minElem.first];//take neighbours
        for (int j = 0; j<adjacents.size(); j++)
        {
            pair<int, int> node = adjacents[j];
            if (!visited[node.first])
            {
                if (distances[node.first]> distances[minElem.first] + node.second) //if we have found smaller distance
                {

                    if (queue.find(make_pair(node.first, distances[node.first])) != queue.end())
                    {
                        queue.erase(make_pair(node.first, distances[node.first]));
                        queue.insert(make_pair(node.first, distances[minElem.first] + node.second));

                    }
                    else
                    {
                        queue.insert(make_pair(node.first, distances[minElem.first] + node.second));
                        cout<<distances[minElem.first] + node.second<<endl;

                    }

                    distances[node.first] = distances[minElem.first] + node.second;
                }

            }

        }
        visited[minElem.first] = true;

    }
    printResults(distances,starting);

}

int main()
{
    int test;
    cin >> test;
    for (int i = 0; i < test; i++)
    {
        int nodes, edges;
        cin >> nodes >> edges;
        vector<vector<pair<int, int>>> vec(nodes + 1);
        for (int j = 0; j < edges; j++)
        {
            int src, des, weight;
            cin >> src >> des >> weight;
            vec[src].push_back(make_pair(des, weight));
            vec[des].push_back(make_pair(src, weight));

        }

        int starting;
        cin >> starting;
        djkstra(vec, starting, nodes);

    }

    system("pause");

    return 0;
}

2 个答案:

答案 0 :(得分:2)

所以,这是你的图表:

您的计划的输出是 Live On Coliru

#include <boost/graph/adjacency_list.hpp>
#include <boost/graph/dijkstra_shortest_paths.hpp>
#include <boost/graph/graph_utility.hpp>
#include <iostream>

using Graph = boost::adjacency_list<
        boost::vecS, boost::vecS, boost::directedS,
        boost::no_property,
        boost::property<boost::edge_weight_t, int>
    >;

int main() {
    std::cin.exceptions(std::ios::failbit);
    int test; std::cin >> test;
    for (int i = 0; i < test; i++) try {
        int nodes, edges;
        std::cin >> nodes >> edges;
        std::cout << "Nodes:" << nodes << " Edges:" << edges << "\n";

        Graph g(nodes + 1);

        for (int j = 0; j < edges; j++) {
            int src, des, weight;
            std::cin >> src >> des >> weight;
            std::cout << "src:" << src << " des:" << des << " weight:" << weight << "\n";

            add_edge(src, des, weight, g);
        }

        boost::print_graph(g);

        int starting;
        std::cin >> starting;

        std::vector<int> dist(num_vertices(g));
        auto id = get(boost::vertex_index, g);

        dijkstra_shortest_paths(g, starting, 
                weight_map(get(boost::edge_weight, g))
                .distance_map(make_iterator_property_map(dist.begin(), id))
            );

        std::cout << "Distances:\n";
        for (Graph::vertex_descriptor v = 0; v < num_vertices(g); v++) {
            std::cout << starting << " -> " << v << " = " << dist[v] << "\n";
        }
    } catch(std::exception const& e) {
        std::cout << "Error: " << e.what() << "\n";
    }
}

打印:

Nodes:6 Edges:7
src:1 des:2 weight:5
src:2 des:3 weight:2
src:3 des:4 weight:3
src:1 des:4 weight:9
src:4 des:5 weight:2
src:5 des:6 weight:3
src:1 des:6 weight:2
0 --> 
1 --> 2 4 6 
2 --> 3 
3 --> 4 
4 --> 5 
5 --> 6 
6 --> 
Distances:
1 -> 0 = 2147483647
1 -> 1 = 0
1 -> 2 = 5
1 -> 3 = 7
1 -> 4 = 9
1 -> 5 = 11
1 -> 6 = 2

这是对的!

这里是算法的输出,通过所有可到达的目标顶点设置动画:

enter image description here

正确的距离实际上应该是不言而喻的:

1: 0  
2: 5  
3: 5+2 = 7  
4: 9 (5+2+3 is larger)  
5: 9+2 = 11  
6: 2 (both 5+2+3+2+3 and 9+2+3 are way larger)

更新未完成

是的,如果它是无向的,the outcome is different

enter image description here

答案 1 :(得分:2)

所以,after尝试tosense of成为question code,我根据the Wikipedia Pseudo-code

进行了干净的重写

(因为你仍然没有显示出你认为错误的输出),你的基本错误只是使用set<>

<强> Bugged Version

#include <algorithm>
#include <iostream>
#include <limits>
#include <list>
#include <queue>
#include <set>
#include <stack>
#include <vector>
#include <cassert>

using Vertex  = unsigned;
using Weight  = double;

struct OutEdge {
    Vertex node;
    Weight weight;
    bool operator<(OutEdge const& o) const { return weight < o.weight; }
};

using OutEdges     = std::vector<OutEdge>;
using AdjList      = std::vector<OutEdges>;
using Distances    = std::vector<Weight>;
using Predecessors = std::vector<Vertex>;

static Weight const INFINITY = std::numeric_limits<Weight>::max();
static Vertex const UNDEFINED {-1u};

using namespace std;

void print_graph(AdjList const& graph) {
    for (Vertex v = 1; v < graph.size(); v++) {
        std::cout << v << " -";
        for (auto& adj : graph[v])
            std::cout << " " << adj.node << "(" << adj.weight << ")";
        std::cout << "\n";
    }
}

void printResults(Distances const& dist, Vertex starting) {
    for (Vertex v = 0; v < dist.size(); v++) {
        std::cout << starting << " -> " << v << ": ";
        if (dist[v] == INFINITY && v != starting) {
            cout << -1 << " ";
        } else { // if (v != starting) {
            cout << dist[v] << " ";
        }
        std::cout << "\n";
    }
}

Distances dijkstra(AdjList const& graph, Vertex source) {
    std::set<OutEdge> Q;

    vector<bool> visited(graph.size(), false);
    Distances    dist(graph.size(), INFINITY);  // Unkown distance from source
    Predecessors prev(graph.size(), UNDEFINED); // Previous node in optimal path from source

    dist[source] = 0; // Distance from source to source

    for (Vertex v = 1; v < graph.size(); v++)
        Q.insert({v, dist[v]});

    while (!Q.empty()) {
        Vertex u = Q.begin()->node;
        visited[u] = true;
        Q.erase(Q.begin());

        for (auto& v : graph[u]) { // for each neighbour
            if (visited[v.node]) // where v is still in Q.
                continue;

            Weight alt = dist[u] + v.weight;

            if (alt < dist[v.node]) // A short path to v has been found
            {
                dist[v.node] = alt;
                prev[v.node] = u;

                // update prio
                auto it = std::find_if(Q.begin(), Q.end(), [&](auto& oe) { return oe.node == v.node; });
                if (it != Q.end()) {
                    Q.erase(it);
                    Q.insert({v.node, alt});
                }
            }
        }
    }

    return dist;
}

int main() {
    size_t test;
    cin >> test;
    for (size_t i = 0; i < test; i++) {
        size_t nodes, edges;
        cin >> nodes >> edges;
        nodes += 1; // hack to avoid converting ids to base-0

        AdjList adj(nodes);
        for (size_t j = 0; j < edges; j++) {
            Vertex src, des;
            Weight weight;

            cin >> src >> des >> weight;
            assert(weight>=0);

            adj[src].push_back({des, weight});
            adj[des].push_back({src, weight});
        }

        print_graph(adj);

        Vertex starting;
        cin >> starting;
        auto d = dijkstra(adj, starting);
        printResults(d, starting);
    }
}

将结果打印为:

1 -> 0: -1 
1 -> 1: 0 
1 -> 2: 5 
1 -> 3: 7 
1 -> 4: 9 
1 -> 5: -1 
1 -> 6: 2 
  

注意 您在djkstra [原文如此]中至少还有一个错误

for (int i = 0; i<number_of_vertices-1; i++)
     

-1似乎是完全错误的(如果有的话,它应该“{应该”+1,尽管有+1遍布所有代码的代码气味很大。看看我在我的版本中如何解决这个问题。

说明

std::set<>为具有唯一元素的容器建模。在这种情况下,重量是等价性质,因此,具有相同重量的所有元素是等同的。在一开始,队列将包含最多2个元素:1个起始顶点(距离0)和1个其他顶点,这些顶点都以相同的距离播种(INFINITY;或max版本)。

由于许多节点从未被访问过(因为队列为空时算法就会停止),因此这会使您的算法下沉。

修正为5个字母:s/set/multiset/

<强> Fixed Version

打印:

1 -> 0: -1 
1 -> 1: 0 
1 -> 2: 5 
1 -> 3: 7 
1 -> 4: 7 
1 -> 5: 5 
1 -> 6: 2