【问题标题】:How can i make this modification on Dijkstra Algorithm more efficient?我怎样才能使对 Dijkstra 算法的修改更有效?
【发布时间】:2020-04-01 09:00:55
【问题描述】:

这个问题是我计算机科学作业的一部分。作业包括 5 种不同类型的学生,它们通过给定的加权无向节点图旅行,其中每个学生都有不同的方法。第五个学生是最难的,我一直没能有效地实施。

第五个学生有一个秘密力量,他可以在相邻节点之间传送,所以在它们之间移动需要0时间。然而,为了给那个秘密力量充电,他需要通过两个边缘,并且他/她在旅程开始时没有那个秘密力量。与其他四个学生不同的是,他可以多次使用同一刃,因此在第一步中,他可能会走 N_1->N_2 和 N_2->N_1 来为他/她的秘密力量充电。 (S)他无法储存他/她的秘密力量,获得后必须立即使用。

第五个学生想知道到达山顶的最短时间。一开始,他没有任何能量,所以他需要通过两个边来给它充电。

我尝试的方法是修改 Dijkstra 算法;它不是一个节点一个节点地移动,而是从一个节点计算所有三个可能的跳跃,只考虑前两个跳跃的权重。它考虑了所有情况,例如去一个节点并返回充电和跳转一个高权重节点。它确实有效,我确实得到了给定测试用例的所有正确答案,但它很慢。我们处于两秒的限制之下,现在我的算法需要大约 4 秒来处理具有 50 000 个节点和 100 000 条边的测试用例。

我猜问题在于到达邻居,因为有 3 个嵌套的 for 循环可以到达所有可能的 3 个跳离邻居(同时也能够多次使用相同的边缘),这基本上使得这个 O(n ^3) (但我不擅长使用 big-oh 符号,所以我不确定它是否真的如此。)

有没有人有任何想法可以让这个算法更高效,或者是一个不那么慢的不同算法?

感谢任何帮助!

如果有帮助的话,这里是代码。

long long int HelpStudents::fifthStudent() { 
auto start = std::chrono::system_clock::now();


set< pair<long long int,int> >setds;
vector<long long int> dist(totalNodes+15,std::numeric_limits<long long int>::max());
setds.insert(make_pair(0,1));
dist[1] = 0;
bool change = false;
int counter = 0;   //these variables were just for checking some things
int max_counter = 0;
int changed_summit = 0;
int operations_after_last_change = 0;


int w1;
int w2;
int db = 0;

vector<int> neighbors;
vector<int> neighbors2;
vector<int> neighbors3;
int u;

while(!setds.empty()){
    pair<long long int,int> tmp = *(setds.begin());
    setds.erase(setds.begin());
    u = tmp.second; //vertex label
    if(dist[u] > dist[summit_no]){
        continue;
    }
    if(!change){
        counter++;
    }else{
        counter = 0;  //debugging stuff
    }
    db++;
    //cout << db2 << endl;

    operations_after_last_change++;
    max_counter = max(counter,max_counter);
    //cout << "counter: " << counter <<endl;
    change = false;
    neighbors = adjacency_map[u];  //adjacency map holds a vector which contains the adjacent nodes for the given key

    //cout << "processing: " << "(" << tmp.first << ","<< tmp.second << ") " << endl;

    for(int nb : neighbors){
        w1 = getWeight(u,nb);  //this is one jump,
        //nb is neighboor
        neighbors2 = adjacency_map[nb];
        //cout << "\t->"  << nb  << endl;
        if( nb == summit_no){
            if(dist[nb] >  dist[u] + (w1)){

                auto t = setds.find(make_pair(dist[nb],nb));
                if(t != setds.end()){
                    setds.erase(t);
                }
                dist[nb] = dist[u] + (w1);
                change = true;
                changed_summit++;
                operations_after_last_change = 0;
                //cout << "changed summit to " << (dist[u] + (w1)) << endl;

                //continue;
            }
        }

        for(int nb2: neighbors2){  //second jump
            w2 = getWeight(nb,nb2);
            //cout << "\t\t->"  << nb2  << endl;
            if( nb2 == summit_no){
                if(dist[nb2] >  dist[u] + (w1+w2)){

                    auto t = setds.find(make_pair(dist[nb2],nb2));
                    if(t != setds.end()){
                        setds.erase(t);
                    }
                    dist[nb2] = dist[u] + (w1+w2);
                    change=true;
                    changed_summit++;
                    operations_after_last_change = 0;
                    //cout << "changed summit to " << (dist[u] + (w1+w2)) << endl;

                    //continue;
                }
            }
            neighbors3 = adjacency_map[nb2];

            for(int nbf: neighbors3){  //third jump, no weight
                //cout << "\t\t\t->"  << nbf;
                if(dist[nbf] >  dist[u] + (w1+w2)){


                    auto t = setds.find(make_pair(dist[nbf],nbf));
                    if(t != setds.end()) {
                        setds.erase(t);
                    }

                    change = true;
                    dist[nbf] = dist[u] + (w1+w2);
                    if(nbf == summit_no){
                        changed_summit++;
                        operations_after_last_change = 0;
                        //cout  << endl;
                    }else{
                        setds.insert(make_pair(dist[nbf],nbf));
                        //cout << "\t\t\t\t inserted ("<<dist[nbf]<<","<<nbf<<")"  << endl;
                    }

                    //cout << "changed " << nbf << " to " << (dist[u] + (w1+w2)) << ";  path: "<< u <<" -> "<<nb<<" -> "<<nb2 << " -> " <<nbf << endl;
                    //setds.insert(make_pair(dist[nbf],nbf));
                }else{
                    //cout  << endl;
                }
            }

        }





    }
}
auto end = std::chrono::system_clock::now();
std::chrono::duration<double> elapsed_seconds = end-start;
cout << "time passed: "<< elapsed_seconds.count() <<"   total loop: "<<db<< endl;
return dist[summit_no];

【问题讨论】:

    标签: algorithm performance c++11 graph-algorithm processing-efficiency


    【解决方案1】:

    您制作(或更可能想象)一个新的有向图,其中包含学生 5 可能处于的每个独特情况/状态的节点——即原始图节点和充电状态的组合 ( 0、1 或 2)。因为有 3 种电荷状态,所以该图的节点数将是原始图的 3 倍。

    然后你在这个新图上使用完全普通的 Dijkstra 算法。

    【讨论】:

    • 现在运行大输入大约需要 2.4 秒,希望我的电脑速度很慢
    • 对于具有 100K 边的图(上述转换后为 300K)而言,这太慢了,除非您的 PC 已使用 30 年
    • 原来它是我使用的集合之一,std::map 自动排序,这会减慢它的速度。切换到无序地图后,速度要快得多。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-12-23
    • 1970-01-01
    • 1970-01-01
    • 2010-11-14
    • 2016-05-08
    • 1970-01-01
    相关资源
    最近更新 更多