マルチスタンダード最適パスでダイクストラアルゴリズムを使用する際の注意点

問題はパットクラスA1018にあります。正しい方法はダイクストラ+ DFSですが、私はダイクストラのみを使用しようとしています。ダイクストラプロセスでは、最適パスの2つの基準が同時に判断され、の再帰が組み合わされます。不足し、頂点間の残りの値。関係、論理エラーを含む次のコードを記述します。

#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;
}

提出後、テストポイント7に合格できませんでしたが、厳密なデータがないNiuke.comはすべて合格できます。
タイトルの最適パスの2つの基準:
①パスの長さが最小、d [u] + graph [u] [j] <d [j]
②①が同じ場合、不足が最小であるか、不足が同じで残りが最小、t_lack <lack [j] ||(t_lack == Rack [j] && t_remain <remain [j])①②
を同時に判断するの誤りは、標準①が頂点、標準の②には再発がありません。
つまり、始点sから頂点kまでのある経路が最短経路であり、sから他の頂点までの経路も最短経路でなければならないことがわかっています。これは、pre配列を使用して複数の最短パスを記録する原理でもあります。
ただし、sからkへの特定のパスが基準②で最適になる場合、sから他の頂点へのパスは必ずしも最適ではありません。
私はこの標準①を再帰的最適パス標準と呼んでいます。このような標準には、最小/最大パス長、最小/最大エッジ重み累積、および最小/最大ポイント重み累積が含まれます。
標準②を再発のない最適経路標準と呼びます。そのような標準が質問にある場合は、ダイクストラ+ DFSを見つけて最短経路を見つけ、次に他の標準に基づいて最適経路を判断する必要があります。
したがって、この質問にダイクストラを使用することはできません。
正しいコード:

#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;
}

ベクトル配列preは逆グラフを構成します。直接アクセスする場合、再帰が戻ったときに記録できる状態は1つだけであり、中央の頂点には複数の状態がある可能性があるため、1回の再帰で完了することはできません。 DFSの再帰では、t_pathを使用してパスを記録し、開始点に到達してから、チェック関数を呼び出して不足と残りを計算し、パスを使用して最終結果を受け取り
ここに画像の説明を挿入
ます。グラフのエッジはすべて一方向であるため、DFSはこのグラフの頂点を記録するためにvis配列を設定する必要はありません。アクセスステータス。
DFSの2つのアイデア:
1。再帰内のパスにポイントを記録してから、再帰のすぐ外側のパスからポイントを削除して、状態を復元します。再帰に入る前は、t_pathには現在の頂点vがなく、再帰的に戻った後、t_pathにはvがあります。
2.入力と削除はすべて再帰的に実行されるため、より優れています。再帰に入る前のt_pathの内容は、再帰的な戻りの後と同じです。

おすすめ

転載: blog.csdn.net/sinat_37517996/article/details/104500274