Points to note when using the dijkstra algorithm in the multi-standard optimal path

The problem comes from pat Class A 1018. The correct way is dijkstra+DFS, but I try to use only dijkstra. In the dijkstra process, the two criteria of the optimal path are judged at the same time, combined with the recursion of the lack and the remaining value between the vertices. Relationship, write the following code with logical errors:

#include <cstdio>
#include <climits>
#include <stack>
#include <algorithm>

using namespace std;

const int maxv = 501;
const int INF = INT_MAX;

int C, N, Sp, M;
int v_weight[maxv];
int graph[maxv][maxv]={
    
    };
int d[maxv];
bool confirmed[maxv]={
    
    };
int lack[maxv]={
    
    }; //使起点到i的最短路上的顶点全部达到perfect还差多少自行车
int remain[maxv]={
    
    }; //带回去多少自行车
int pre[maxv]; //记录路径

void init();
void dijkstra();

int main(){
    
    
    scanf("%d%d%d%d", &C, &N, &Sp, &M);
    for(int i=1; i<=N; i++) scanf("%d", &v_weight[i]);
    while(M--){
    
    
        int si, sj, w;
        scanf("%d%d%d", &si, &sj, &w);
        graph[si][sj] = graph[sj][si] = w;
    }
    init();
    dijkstra();

    printf("%d ", lack[Sp]);
    stack<int> path;
    int v = Sp;
    while(v!=0){
    
    
        path.push(v);
        v = pre[v];
    }
    path.push(0);
    int n = path.size();
    while(n--){
    
    
        printf("%d", path.top());
        path.pop();
        if(n>0) printf("->");
    }
    printf(" %d", remain[Sp]);

    return 0;
}

void init(){
    
    
    fill(d, d+N+1, INF);
    d[0] = 0;
    for(int i=0; i<=N; i++) pre[i] = i;
    return;
}

void dijkstra(){
    
    
    for(int k=0; k<=N; k++){
    
    
        int u=-1, min_d=INF;
        for(int i=0; i<=N; i++){
    
    
            if(!confirmed[i] && d[i]<min_d){
    
    
                u = i;
                min_d = d[i];
            }
        }
        if(u==-1) return;
        confirmed[u] = true;
        for(int j=0; j<=N; j++){
    
    
            if(!confirmed[j] && graph[u][j]!=0){
    
    
                if(d[u]+graph[u][j]<d[j]){
    
    
                    d[j] = d[u] + graph[u][j];
                    //相邻点的递推
                    int need = (C/2) - v_weight[j];
                    if(need>0){
    
    
                        //使j达到perfect缺车
                        if(remain[u]>=need){
    
    
                            //到u结束时,u以前的顶点全部达到perfect后,此时多余的车可以cover
                            lack[j] = lack[u];
                            remain[j] = remain[u] - need;
                        }
                        else{
    
    
                            //此时多余的车不够cover
                            lack[j] = lack[u] + need - remain[u];
                            remain[j] = 0;
                        }
                    }
                    else{
    
    
                        //使j达到perfect需要带走车
                        lack[j] = lack[u];
                        remain[j] = remain[u] + v_weight[j] - (C/2);
                    }
                    pre[j] = u;
                }
                else if(d[u]+graph[u][j]==d[j]){
    
    
                    //计算如果走这条备选路,到j处的lack和remain,然后以第二标准作比较
                    int t_lack, t_remain;
                    int need = (C/2) - v_weight[j];
                    if(need>0){
    
    
                        if(remain[u]>=need){
    
    
                            t_lack = lack[u];
                            t_remain = remain[u] - need;
                        }
                        else{
    
    
                            t_lack = lack[u] + need - remain[u];
                            t_remain = 0;
                        }
                    }
                    else{
    
    
                        t_lack = lack[u];
                        t_remain = remain[u] + v_weight[j] - (C/2);
                    }
                    if(t_lack<lack[j] || (t_lack==lack[j] && t_remain<remain[j])){
    
    
                        lack[j] = t_lack;
                        remain[j] = t_remain;
                        pre[j] = u;
                    }
                }
            }
        }
    }
    return;
}

After submission, test point 7 cannot be passed, but Niuke.com, which has less rigorous data, can pass all of them.
The two criteria for the optimal path in the title:
①The path length is the smallest, d[u]+graph[u][j]<d[j]
②When ① the same, the lack of the smallest or the same lack of the remain the smallest, t_lack<lack[ j] || (t_lack==lack[j] && t_remain<remain[j])
The error of judging ①② at the same time is that the standard ① has recurrence between vertices, while the standard ② does not have recurrence.
That is, it is known that a certain path from the starting point s to the vertex k is the shortest path, then the path from s to other vertices must also be the shortest path. This is also the principle of using the pre array to record multiple shortest paths.
However, if a certain path from s to k makes it optimal on the criterion ②, the path from s to other vertices is not necessarily optimal.
I call the standard ① the recursive optimal path standard. Such standards include the smallest/large path length, the smallest/large edge weight accumulation, and the smallest/large point weight accumulation.
Call standard ② as the optimal path standard without recurrence. If such a standard appears in the question, then dijkstra+DFS must be found to find the shortest path first, and then judge the optimal path based on other standards.
So this question can't just use dijkstra.
Correct code:

#include <cstdio>
#include <climits>
#include <vector>
#include <algorithm>

using namespace std;

const int maxv = 501;
const int INF = INT_MAX;

int C, N, Sp, M;
int v_weight[maxv];
int graph[maxv][maxv]={
    
    };
int d[maxv];
bool confirmed[maxv]={
    
    };
vector<int> pre[maxv]; //最短路径不唯一

int f_lack = INF, f_remain = INF; //最优值
vector<int> path, t_path;

void init();
void dijkstra();
void DFS(int v);
void check();

int main(){
    
    
    scanf("%d%d%d%d", &C, &N, &Sp, &M);
    for(int i=1; i<=N; i++) scanf("%d", &v_weight[i]);
    while(M--){
    
    
        int si, sj, w;
        scanf("%d%d%d", &si, &sj, &w);
        graph[si][sj] = graph[sj][si] = w;
    }
    init();
    dijkstra(); //先找最短路径
    DFS(Sp); //从这些最短路径中,以第二标准找到最优路径
    printf("%d ", f_lack);
    int n = path.size();
    for(int i=n-1; i>=0; i--){
    
    
        printf("%d", path[i]);
        if(i>0) printf("->");
    }
    printf(" %d", f_remain);

    return 0;
}

void init(){
    
    
    fill(d, d+N+1, INF);
    d[0] = 0;
    return;
}

void dijkstra(){
    
    
    for(int k=0; k<=N; k++){
    
    
        int u=-1, min_d=INF;
        for(int i=0; i<=N; i++){
    
    
            if(!confirmed[i] && d[i]<min_d){
    
    
                u = i;
                min_d = d[i];
            }
        }
        if(u==-1) return;
        confirmed[u] = true;
        for(int j=0; j<=N; j++){
    
    
            //u->j
            if(!confirmed[j] && graph[u][j]!=0){
    
    
                if(d[u]+graph[u][j]<d[j]){
    
    
                    d[j] = d[u] + graph[u][j];
                    pre[j].clear();
                    pre[j].push_back(u);
                }
                else if(d[u]+graph[u][j]==d[j]){
    
    
                    pre[j].push_back(u);
                }
            }
        }
    }
    return;
}

void DFS(int v){
    
    
    t_path.push_back(v); //在递归内加入路径
    if(v==0){
    
    
        check();
        return;
    }
    int n = pre[v].size();
    for(int i=0; i<n; i++){
    
    
        DFS(pre[v][i]);
        t_path.pop_back(); //在递归外紧接着从路径中删去,还原状态。
    }
    return;
}

void check(){
    
    
    int n = t_path.size();
    int lack=0, remain=0;
    for(int i=n-2; i>=0; i--){
    
    
        int v = t_path[i];
        int need = (C/2) - v_weight[v];
        if(need>0){
    
    
            //使v达到perfect缺车
            if(remain>=need){
    
    
                //上一个点的remain足够cover
                remain -= need;
            }
            else{
    
    
                //上一个点的remain不够cover
                lack = lack + need - remain;
                remain = 0;
            }
        }
        else{
    
    
            //使v达到perfect要拿走车
            remain = remain - need;
        }
    }
    if(lack<f_lack || (lack==f_lack && remain<f_remain)){
    
    
        f_lack = lack;
        f_remain = remain;
        path = t_path;
    }
    return;
}

The vector array pre constitutes an inverted graph. If it is to be accessed directly, it cannot be completed in one recursion, because only one state can be recorded when the recursion returns, and the middle vertex may have multiple states, so it needs to be divided into two steps. In the recursion of DFS, use t_path to record the path, reach the starting point and then call the check function to calculate the lack and remain, and use path to receive the final result
Insert picture description here
. The edges in the graph are all one-way, so DFS does not need to set the vis array to record the vertices for this graph. Access status.
Two DFS ideas:
1. Record the point in the path within the recursion, and then delete the point from the path immediately outside the recursion to restore the state. Before entering recursion, t_path has no current vertex v, and after recursive return, t_path has v.
2. Entry and deletion are all performed recursively, which is better. Before entering recursion, the content of t_path is the same as after recursive return

Guess you like

Origin blog.csdn.net/sinat_37517996/article/details/104500274