如何使对Dijkstra算法的修改更有效?

时间:2019-12-07 18:06:53

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

问题是我的计算机科学作业的一部分。作业包括5种不同类型的学生,它们遍历给定的加权无向节点图,每个学生使用不同的方法。第五名学生是最​​难的一个人,我无法有效地实施它。

  

第五个学生拥有秘密力量,可以在相邻节点之间传送,因此在它们之间移动需要0倍的时间。但是,要充实这个秘密力量,他需要穿越两个边缘,并且他/她在旅途的开始就没有那个秘密力量。与其他四个学生不同,他可以多次使用同一条边,因此在第一步中,他可以走N_1-> N_2和N_2-> N_1来恢复他/她的秘密力量。 (S)他无法存储他/她的秘密权力,获得权力后必须立即使用。

     

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

我尝试的方法是Dijkstra算法的修改;而不是逐个节点移动,而是从一个节点计算所有三个可能的跳转,而只考虑前两个跳转的权重。它考虑了所有情况,例如去节点再返回以重新充电并跳出高权重节点。它确实可以工作,而且我确实可以为给定的测试用例获得所有正确答案,但是速度很慢。我们处于2秒的限制之下,现在,对于具有50000个节点和100000条边的测试用例,我的算法需要大约4秒钟的时间。

我猜测问题出在邻居之间,因为有3个嵌套的for循环可以到达所有可能的3个跳跃邻居(同时也能够多次使用相同的边),这基本上使这个O(n ^ 3)(但是我对大哦表示法不太满意,因此不确定是否确实如此。)

有人有什么想法可以提高这种算法的效率,或者有其他算法没那么慢吗?

感谢您的帮助!

如果有帮助,请参见以下代码。

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];

1 个答案:

答案 0 :(得分:0)

您制作(或更可能想象)一个新的有向图,其中有一个学生5可能处于的每个独特情况/状态的节点-即原始图节点和收费状态的组合( 0、1或2)。由于存在3个电荷状态,因此该图的节点数是原始图的3倍。

然后,在此新图中使用完全普通的Dijkstra算法。