【发布时间】: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