问题是我的计算机科学作业的一部分。作业包括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];
答案 0 :(得分:0)
您制作(或更可能想象)一个新的有向图,其中有一个学生5可能处于的每个独特情况/状态的节点-即原始图节点和收费状态的组合( 0、1或2)。由于存在3个电荷状态,因此该图的节点数是原始图的3倍。
然后,在此新图中使用完全普通的Dijkstra算法。