概要要約グラフ理論の問題(1)
以下のためにグラフ理論、我々はアルゴリズムがより精通で尊重、この時間は、私は、共通の焦点アルゴリズムを発見しました。
- 最短パスアルゴリズム(ダイクストラ、SPFE、フロイド)
- 単一ソース最短アルゴリズムダイクストラ
まず、このアルゴリズムは、別の点への最短経路の一点を計算するのに適しており、アルゴリズムは、負のリングであってはなりません。示すように、アルゴリズムの遅い速度は、その後、わずかな問題のために眠る:
この図は、アルゴリズムを求めての基本的な考え方です。
アルゴリズムのプロセス:
- ノードからノードの最も近い点を見つけ、それが収集U.に、彼をマークします
- 点Uは、Uがセットされていない見つけ、フタル点エッジが接続されて接続されています。
- V = U、検索が終了すると、最終的に終了プロセスを導くために、前の操作を繰り返します。
このアルゴリズムのアルゴリズムのフローチャート
https://wenku.baidu.com/view/8a5c11303968011ca300916a.html
参照コード:
#include <iostream>
#include <cstring>
using namespace std;
const int N = 1e3 + 9;
const int M = 1e4 + 9;
const int inf = 0x3f3f3f3f;
struct edge {
int v, w, next;
edge() {}
edge(int _v, int _w, int _next) {
v = _v;
w = _w;
next = _next;
}
} e[M << 1];
int head[N], len;
void init() {
memset(head, -1, sizeof head);
len = 0;
}
void add(int u, int v, int w) {
e[len] = edge(v, w, head[u]);
head[u] = len++;
}
void add2(int u, int v, int w) {
add(u, v, w);
add(v, u, w);
}
int n, m;
int dis[N];
bool vis[N];
void dijkstra(int u) {
memset(vis, false, sizeof vis);
memset(dis, inf, sizeof dis);
dis[u] = 0;
for (int i = 0; i < n; ++i) {
int mi = inf;
for (int j = 1; j <= n; ++j) {
if (!vis[j] && dis[j] < mi) {
mi = dis[u = j];
}
}
if (mi == inf) {
return;
}
vis[u] = true;
for (int j = head[u]; ~j; j = e[j].next) {
int v = e[j].v;
int w = e[j].w;
if (!vis[v] && dis[v] > dis[u] + w) {
dis[v] = dis[u] + w;
}
}
}
}
int main() {
init();
int u, v, w;
cin >> n >> m;
while (m--) {
cin >> u >> v >> w;
add2(u, v, w);
}
dijkstra(1);
cout << dis[n] << endl;
return 0;
}
これは、コードのちょうど基本的な流れである、あなたは、修正を行う、テンプレートに基づいてブラシをかけることができます。
基本的な考え方は、エンドポイントにまで延長中心アウト層(BFS +貪欲)の拡張のための出発点です。
これが私たちのアルゴリズムであるため、多くの問題を解決することができます。
3 3
1 2 5
2 3 5
3 1 2
データを入力し、何が起こるかを参照してください!
- ヒープ最適化ダイクストラ
正まばらなグラフのためのアルゴリズム、我々が使用して最適化。
#include <iostream>
#include <cstring>
#include <set>
using namespace std;
const int N = 1e3 + 9;
const int M = 1e4 + 9;
const int inf = 0x3f3f3f3f;
typedef pair<int, int> pall;
#define X first
#define Y second
struct edge {
int v, w, next;
edge() {}
edge(int _v, int _w, int _next) {
v = _v;
w = _w;
next = _next;
}
} e[M << 1];
int head[N], len;
void init() {
memset(head, -1, sizeof head);
len = 0;
}
void add(int u, int v, int w) {
e[len] = edge(v, w, head[u]);
head[u] = len++;
}
void add2(int u, int v, int w) {
add(u, v, w);
add(v, u, w);
}
int n, m;
int dis[N];
bool vis[N];
void dijkstra(int u) {
memset(vis, false, sizeof vis);
memset(dis, inf, sizeof dis);
dis[u] = 0;
}
int main() {
init();
int u, v, w;
cin >> n >> m;
while (m--) {
cin >> u >> v >> w;
add2(u, v, w);
}
dijkstra(1);
cout << dis[n] << endl;
return 0;
}
私たちはすることができ、ヒープ要素のトップを取得する必要があり、最小を見つけ、ここで通常のダイクストラ差の中核である(、自動的にソートヒープ
先頭の要素をスタックすることは私たちに必要な最小です)。その後、我々は(マークがコレクションの意味に追加された)コレクションにこの要素を置きます。
-
SPFA単一始点最短経路アルゴリズム
I、付加的な使用は、キューの頂点今後の展開保持する最短の頂点に光源からのジ表現を使用SPFAアルゴリズムで、
リストを、iがキューにない頂点を識別するためにingiを使用します。
図:
スペースSPFAの複雑さはビットビートステップスパース図のように、O(V)です。彼は、キューを実行するために使用され、そのため、SPEAはちょうど次の列ではなく、非常に多くの種類をアップグレードすることです。ある程度まで、それはアイデアから変換されBFS SPFAで考えることができます。右辺から右側に又はBFS図単位長さを除く、重み付きグラフの拡張は、SPFAました。ステップアルゴリズムビート:
-
拡張複数のキューを保持するためのキューを使用しました。
-
一つの要素を持つキューは、リラクゼーションや他のポイントを取ります。
-
すべてのポイントがキューにキューに含まれていない場合は、その後、プログラムは終了し、アルゴリズムが完了しています。
bool inq[MAX_N];
int d[MAX_N]; // 如果到顶点 i 的距离是 0x3f3f3f3f,则说明不存在源点到 i 的最短路
void spfa(int s) {
memset(inq, 0, sizeof(inq));
memset(d, 0x3f, sizeof(d));
d[s] = 0;
inq[s] = true;
queue<int> q;
q.push(s);
while (!q.empty()) {
int u = q.front();
q.pop();
inq[u] = false;
for (int i = p[u]; i != -1; i = e[i].next) {
int v = e[i].v;
if (d[u] + e[i].w < d[v]) {
d[v] = d[u] + e[i].w;
if (!inq[v]) {
q.push(v);
inq[v] = true;
}
}
}
}
}
図アルゴリズム:https://i.loli.net/2019/06/09/5cfcb3db1267062487.jpg
コード上はSPEAの最初のアルゴリズムと同様の構成で初期化、すべての構造において同一です。
負のリング解析 SPEAアルゴリズム:
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e3 + 9;
const int M = 1e4 + 9;
const int inf = 0x3f3f3f3f;
struct edge {
int v, w, next;
edge() {}
edge(int _v, int _w, int _next) {
v = _v;
w = _w;
next = _next;
}
} e[M << 1];
int head[N], len;
void init() {
memset(head, -1, sizeof head);
len = 0;
}
void add(int u, int v, int w) {
e[len] = edge(v, w, head[u]);
head[u] = len++;
}
void add2(int u, int v, int w) {
add(u, v, w);
add(v, u, w);
}
int n, m;
int main() {
init();
int u, v, w;
cin >> n >> m;
while (m--) {
cin >> u >> v >> w;
add2(u, v, w);
}
return 0;
}
その後、プログラムを実行することができます。
3 3
1 2 5
2 3 5
3 1 2
- フロイドマルチソース最短パスアルゴリズム
重み付きグラフを計算するためのフロイドのアルゴリズムは、任意の2つの頂点のアルゴリズム間の最短経路を与えられます。単独に比べ繰り返し数回-ソース
最短パスアルゴリズムは、フロイドは、より一般的に使用される最短経路グラフ理論のトピックを解くことで、ショートコードの利点は、高い効率を有します。
フロイドのアルゴリズムは、図または不在にマルチソースとの最短経路問題に(任意の2点間の最短経路を計算することができる)負環を解決します。多くの場合、
O(N2)、O(N2)の空間的な複雑さの隣接行列記憶、時間複雑。
アルゴリズムの道:
- そうでない場合には最初のポイントを介して、それはDP [K-1] [I] [J]です。
- 最初の点以降場合は、DP [K-1] [I] [J] + DP [K-1] [I] [J]です。
だから、次のようになります。
dp[k][i][j] = min(dp[k − 1][i][j], dp[k − 1][i][k] + dp[k − 1][k][j])
だから、次のコードは次のようになります。
int g[N][N]; // 邻接矩阵存图
int dp[N][N][N];
void floyd(int n) {
for (int k = 0; k <= n; ++k) {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
if (k == 0) {
dp[k][i][j] = g[i][j];
} else {
dp[k][i][j] = min(dp[k - 1][i][j], dp[k - 1][i][k] + dp[k - 1][k][j]);
}
}
}
}
}
我々は一般的に1次元を最適化するために、書面で使用されているフロイドの最終形態を、書きます。そして、簡単に書きます。ここで注意しなければなりませんが
意図され、ほとんどの中間点を列挙するために書かなければなりません。これに全く注意、負担し、その後、3回ミスを注文するのは簡単ではありません
間違っている果物を。
ただ、分析結果:
int g[N][N];
void floyd(int n) {
for (int k = 1; k <= n; ++k) {
for (int i = 1; i <= n; ++i) {
for (int j = 1; j <= n; ++j) {
g[i][j] = min(g[i][j], g[i][k] + g[k][j]);
}
}
}
}
アルゴリズムは、ここで紹介する、完全な横にある差動拘束システムを。
- 差動拘束システム
我々は、差動拘束システムを解く場合は、グラフ理論においてシングルソースの最短(または最長の道路)問題に変換することができます。
それはそうすることができ、負のリングを持っています。
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e3 + 9;
const int M = 1e4 + 9;
const int inf = 0x3f3f3f3f;
struct edge {
int v, w, next;
edge() {}
edge(int _v, int _w, int _next) {
v = _v;
w = _w;
next = _next;
}
} e[M << 1];
int head[N], len;
void init() {
memset(head, -1, sizeof head);
len = 0;
}
void add(int u, int v, int w) {
e[len] = edge(v, w, head[u]);
head[u] = len++;
}
void add2(int u, int v, int w) {
add(u, v, w);
add(v, u, w);
}
int n, m;
int dis[N], in[N];
bool vis[N];
bool spfa(int u) {
memset(vis, false, sizeof vis);
vis[u] = true;
memset(dis, -1, sizeof dis);
dis[u] = 0;
memset(in, 0, sizeof in);
in[u] = 1;
queue<int> q;
q.push(u);
while (!q.empty()) {
u = q.front();
q.pop();
vis[u] = false;
for (int j = head[u]; ~j; j = e[j].next) {
int v = e[j].v;
int w = e[j].w;
if (dis[v] < dis[u] + w) { // 求最长路,和求最短路相反
dis[v] = dis[u] + w;
if (!vis[v]) {
q.push(v);
vis[v] = true;
++in[v];
if (in[v] > n + 1) {
return true;
}
}
}
}
}
return false;
}
int main() {
init();
int u, v, w, op;
cin >> n >> m;
while (m--) {
cin >> op;
cin >> u >> v >> w;
}
if (op == 1) {
add(u, v, -w);
}else if (op == 2) {
add(v, u, w);
}else {
add(u, v, -w);
add(v, u, w);
}
for (int i = 1; i <= n; ++i) {
add(0, i, 0);
}
if (spfa(0)) {
cout << "no" << endl;
}else {
for (int i = 1; i <= n; ++i) {
cout << "x" << i << " = " << dis[i] << endl;
}
}
return 0;
}
これは、今、あなたのプログラムを実行し、差分制約であります:
4 3
1 1 2 3
2 3 2 2
3 3 4 1
これが完了最短期間で、次の段階(オイラー)。
現在、グラフ理論、次のさよならの終わり!