グラフ理論 - K短絡

[概要]

Kいわゆる短絡問題は、手段は、第1ショートパスK、テープエッジがSからT及び所有するように求め、Tを与えるために始点Sと終点を与え、有向グラフmは正の重みn個の点を与えています長さ。

溶液K最短経路問題、二つあり、一つは使用することである*アルゴリズムAとの最短経路アルゴリズムを使用している他の解決のために永続的なヒープと最短パスアルゴリズムを解決します。

[A *アルゴリズム]

:ダイクストラ法については、結論があるチームのk番目の点、経路長sは、その最初のkに短絡された場合には

しかし、その状態の数を最適化するために、A *アルゴリズムを利用するには、MLE原因に直接書き込むことができます。

まず、抗図は、最短、次いで現在のテープの最短距離プラスステージの端部の長さAとして最適化することである各点Tまでの距離を取得するために、最短パスアルゴリズムを実行します*

k番目のチームのポイントは、答えは優先順位にこの点を、ある場合したがって、チームのうちのk番目の端部は、この点への答えは、パスを取るされている場合

struct Edge {
    int to, next;
    int w;
    Edge() {}
    Edge(int to, int next, int w) : to(to), next(next), w(w) {}
};
struct Map {
    int tot;
    int head[N];
    Edge edge[N * N];
    Map() {
        tot = 0;
        memset(head, -1, sizeof(head));
    }
    void addEdge(int x, int y, int w) {
        edge[++tot].to = y;
        edge[tot].next = head[x];
        edge[tot].w = w;
        head[x] = tot;
    }
    Edge &operator[](int pos) { return edge[pos]; }
};
int n, m;
Map G, GT;
int dis[N];
bool vis[N];
struct Status{
    int node;//点编号
    int diss;//距离
    int priority;//优先级
    Status() : node(0), diss(0), priority(dis[0]) {}
    Status(int node, int diss) : node(node), diss(diss), priority(diss + dis[node]) {}
    bool operator<(Status b) const { return priority > b.priority; }
} status;
bool SPFA(int S, int T, int k) { //对反图求最短路
    memset(dis, INF, sizeof(dis));
    memset(vis, false, sizeof(vis));
    dis[S] = 0;

    queue<int> Q;
    Q.push(S);
    while (!Q.empty()) {
        int x = Q.front();
        Q.pop();
        vis[x] = false;
        for (int i = GT.head[x]; i != -1; i = GT[i].next) {
            int y = GT[i].to;
            if (dis[x] + GT[i].w < dis[y]) {
                dis[y] = dis[x] + GT[i].w;
                if (!vis[y]) {
                    Q.push(y);
                    vis[y] = true;
                }
            }
        }
    }
    return dis[T] != INF;
}
int label[N];
int AStart(int S, int T, int k) {
    memset(label, 0, sizeof(label));
    if (S == T)
        k++;

    priority_queue<Status> Q;
    Q.push(Status(S, 0));
    while (!Q.empty()) {
        Status temp = Q.top();
        Q.pop();
        label[temp.node]++;
        if (temp.node == T && label[temp.node] == k)
            return temp.diss;
        for (int i = G.head[temp.node]; i != -1; i = G[i].next)
            if (label[G[i].to] < k)
                Q.push(Status(G[i].to, temp.diss + G[i].w));
    }
    return -1;
}
int main() {
    scanf("%d%d", &n, &m);
    for (int i = 1; i <= m; i++) {
        int x, y, w;
        scanf("%d%d%d", &x, &y, &w);
        G.addEdge(x, y, w);
        GT.addEdge(y, x, w);
    }

    int s, t, k;
    scanf("%d%d%d", &s, &t, &k);
    if (!SPFA(t, s, k))
        printf("-1\n");
    else
        printf("%d\n", AStart(s, t, k));
    return 0;
}

[月]永続的なヒープ

次いで、最短パスのスパニングツリールートとしてTを与えるために、最短パスアルゴリズムを実行抗図の確立を考えると、非ツリーエッジダウン意志従ってDIS [V] -dis路長(u、v)は、得られた増加[U ] + W

1つのパスの場合、我々はそれが非木のエッジを下に渡し回し、得られ合意されたシーケンスは、このパス非ツリーエッジ配列です。

正規非ツリーエッジ配列について考える、我々は対応するTパスにのみSを見つけることができ、従って、kは短絡kの長さプラスT.に小さなコストと最短長さSに等しいです。

合法的な非ツリーエッジ列を取得する方法を考えてみましょう:

  1. パス内の非ツリーエッジはTのルートに現在の点pで開始点を見つけるために
  2. pは最後のこちら側に等しいましょう

そのたびにことを私たちは、このメソッドを介してすべての非ツリーエッジシーケンスを得ることができますが、ショート回路は、第1のxを検索し、状態を展開し、維持するためのプライオリティキューを使用しますが、展開するときに我々はそう、非ツリーエッジシーケンスのすべてを必要としませんO(M)の時間計算まで、あなたは合計時間の複雑さO(mklog⁡(MK))に到達することができます。

この時間の複雑さは、それが状態で使用されているため、許容できない、高すぎる非常に小さく、シーケンスによるチームアウト時に非ツリーエッジ、コストとそれが役に立つかもしれないよりも、それは大きいです、それゆえ、非木のエッジを考慮すること以下の方法新しいシーケンスデキューシーケンスによって得られます:

  1. 追加操作:非ツリーエッジの終端がVである場合、最小コストのV tまでの経路上の配列の出発点を見つけるためにツリーの非現在の側は、非ツリーエッジで添加されます
  2. あるいは操作:最後の非ツリーエッジが非ツリーエッジを交換するコストよりも大きいです

以下は、オレンジの点線は、非ツリーエッジによって置き換えられ、木の新しい紫側の無添加

永続的なヒープを使用する場合、交換作業のために、エッジのルートパスに開始点にUを非ツリーのコストを維持するために、データ構造を保持することができるそのうちのいくつか、最後の非ツリーエッジがそれようにスタックを置き換えを考えますその子ノードの左側と右側を表し

struct Node{
    int val,to;
    Node *left,*right;
    Node(){}
    Node(int val, int to, Node *left, Node *right) : val(val), to(to), left(left), right(right) {}
};
#define Limit 1000000
Node pool[Limit];
Node *top = pool;
Node *newNode(int val, int to) {
    if (top >= pool + Limit)
        return new Node(val, to, NULL, NULL);
    top->val = val;
    top->to = to;
    top->left = NULL;
    top->right = NULL;
    return top++;
}
Node *meGTe(Node *a, Node *b) {
    if (!a)
        return b;
    if (!b)
        return a;
    if (a->val > b->val)
        swap(a, b);
    Node *p = newNode(a->val, a->to);
    p->left = a->left;
    p->right = a->right;
    p->right = meGTe(p->right, b);
    swap(p->left, p->right);
    return p;
}
struct Status {
    int dist;
    Node *p;
    Status(){}
    Status(int dist, Node *p) : dist(dist), p(p) {}
    bool operator<(Status b) const { return dist > b.dist; }
};
struct Edge {
    int to, next;
    int w;
    Edge() {}
    Edge(int to, int next, int w) : to(to), next(next), w(w) {}
};
struct Map {
    int tot;
    int *head;
    Edge *edge;

    Map() {}
    Map(int n, int m) : tot(0) {
        head = new int[(n + 1)];
        edge = new Edge[(m + 5)];
        memset(head, 0, sizeof(int) * (n + 1));
    }
    void addEdge(int x, int y, int w) {
        edge[++tot].to = y;
        edge[tot].next = head[x];
        edge[tot].w = w;
        head[x] = tot;
    }
    Edge &operator[](int pos) { return edge[pos]; }
};
int n, m;
int s, t, k;
Map G, GT;
bool *vis;
int *dis, *pre;
queue<int> SPFA(int S) {
    vis = new bool[(n + 1)];
    dis = new int[(n + 1)];
    pre = new int[(n + 1)];

    memset(dis, INF, sizeof(int) * (n + 1));
    memset(vis, false, sizeof(bool) * (n + 1));

    queue<int> Q;
    Q.push(S);
    dis[S] = 0;
    pre[S] = 0;
    while (!Q.empty()) {
        int x = Q.front();
        Q.pop();
        vis[x] = false;
        for (int i = GT.head[x]; i; i = GT[i].next) {
            int y = GT[i].to;
            int w = GT[i].w;
            if (dis[x] + w < dis[y]) {
                dis[y] = dis[x] + w;
                pre[y] = i;
                if (!vis[y]) {
                    vis[y] = true;
                    Q.push(y);
                }
            }
        }
    }
    return Q;
}
Node **Hash;
void rebuild(queue<int> Q) { //建堆
    for (int i = 1; i <= n; i++) {
        for (int j = G.head[i]; j; j = G[j].next) {
            int to = G[j].to;
            if (pre[i] != j)
                G[j].w += dis[to] - dis[i];
        }
    }

    Hash = new Node *[(n + 1)];
    Q.push(t);
    Hash[t] = NULL;
    while (!Q.empty()) {
        int x = Q.front();
        Q.pop();
        if (pre[x])
            Hash[x] = Hash[G[pre[x]].to];
        for (int i = G.head[x]; i; i = G[i].next)
            if (pre[x] != i && dis[G[i].to] != INF)
                Hash[x] = meGTe(Hash[x], new Node(G[i].w, G[i].to, NULL, NULL));

        for (int i = GT.head[x]; i; i = GT[i].next) {
            int y = GT[i].to;
            if (pre[y] == i)
                Q.push(y);
        }
    }
}
int kthPath(int k) {
    if (s == t)
        k++;
    if (dis[s] == INF)
        return -1;
    if (k == 1)
        return dis[s];

    priority_queue<Status> Q;
    if (!Hash[s])
        return -1;

    Q.push(Status(Hash[s]->val, Hash[s]));
    while (--k && !Q.empty()) {
        Status x = Q.top();
        Q.pop();

        if (k == 1)
            return x.dist + dis[s];

        int y = x.p->to;
        if (Hash[y])
            Q.push(Status(x.dist + Hash[y]->val, Hash[y]));
        if (x.p->left)
            Q.push(Status(x.dist - x.p->val + x.p->left->val, x.p->left));
        if (x.p->right)
            Q.push(Status(x.dist - x.p->val + x.p->right->val, x.p->right));
    }
    return -1;
}
int main() {
    scanf("%d%d", &n, &m);
    G = Map(n, m);
    GT = Map(n, m);
    for (int i = 1, u, v, w; i <= m; i++) {
        scanf("%d%d%d", &u, &v, &w);
        G.addEdge(u, v, w);
        GT.addEdge(v, u, w);
    }
    scanf("%d%d%d", &s, &t, &k);
    queue<int> Q = SPFA(t);
    rebuild(Q);
    printf("%d\n", kthPath(k));
    return 0;
}

[例]

リリース1871元の記事 ウォンの賞賛702 ビュー194万+

おすすめ

転載: blog.csdn.net/u011815404/article/details/102501921