\(Floyed\)三重循環暴力處理node
Floyed經典題ios
題目有億點難壓縮
solution :
出題人良心,看到這個條件能夠考慮\(FLoyed\)git
#include <cmath> #include <cstdio> #include <iostream> using namespace std; const int inf = 1e9; int n, m, ti[300], dis[300][300]; void floyed(int k) { for (int i = 0; i < n; i++) for (int j = 0; j < n; j++) { dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); } } } int main() { scanf("%d%d", &n, &m); for (int i = 0; i < n; i++) scanf("%d", &ti[i]); for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { if (i == j) continue; dis[i][j] = inf; } } int u, v, w; for (int i = 1; i <= m; i++) { scanf("%d%d%d", &u, &v, &w); dis[u][v] = dis[v][u] = w; } int q, x, y, t, head = 0; scanf("%d", &q); for (int i = 1; i <= q; i++) { scanf("%d%d%d", &x, &y, &t); while (ti[head] <= t && head < n) { floyed(head); head++; } if (dis[x][y] == inf || ti[x] > t || ti[y] > t) printf("-1\n"); else printf("%d\n", dis[x][y]); } }
code數組
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <string> #define ll long long const ll inf = 1e13; ll n, m, u, v, w, ans = inf; ll dis[128][128]; ll e[128][128]; using namespace std; ll read() { ll s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } int main() { cin >> n >> m; for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) if (i != j) dis[i][j] = e[i][j] = inf; for (int i = 1; i <= m; i++) { cin >> u >> v >> w; e[u][v] = dis[u][v] = min(dis[u][v], w); e[v][u] = dis[v][u] = min(dis[v][u], w); for (int k = 1; k <= n; k++) { for (int i = 1; i < k; i++) for (int j = i + 1; j < k; j++) ans = min(ans, dis[i][j] + e[i][k] + e[k][j]); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]); dis[j][i] = dis[i][j]; } } if (ans == inf) cout << "No solution."; else cout << ans; return 0; }
求最小環的路徑網絡
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <string> #include <vector> #define ll long long using namespace std; const int N = 1000; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } int e[N][N],pos[N][N],dis[N][N]; vector<int> path; int n ,m; ll ans = 0x3f3f3f3f; void get_path(int x, int y) { if (!pos[x][y]) return; get_path(x, pos[x][y]); path.push_back(pos[x][y]); get_path(pos[x][y], y); } void Floyed() { for (int k = 1; k <= n; k++) { for (int i = 1; i < k; i++) { for (int j = i+1; j < k; j++) { if ((ll)dis[i][j] + e[i][k] + e[k][j] < ans) { ans = dis[i][j] + e[i][k] + e[k][j]; path.clear(); path.push_back(k); path.push_back(i); get_path(i, j); path.push_back(j); } } } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if (dis[i][j] > dis[i][k] + dis[k][j]) { dis[i][j] = dis[i][k] + dis[k][j]; pos[i][j] = k; } } } } } int main() { n = read(), m = read(); memset(e, 63, sizeof(e)); for (int i = 1; i <= n; i++) e[i][i] = 0; for (int i = 1, u, v, w; i <= m; i++) { u = read(), v = read(), w = read(); e[u][v] = e[v][u] = min(e[u][v], w); } memcpy(dis, e, sizeof(e)); Floyed(); if (ans == 0x3f3f3f3f) { puts("No solution."); system("pause"); return 0; } else { for (int i = 0; i < path.size(); i++) printf("%d ", path[i]); } system("pause"); return 0; }
最短路貌似是我最早學的圖論問題,寫了寫貌似也不知道咋會的了,貌似就只會背板子了hhh,並且\(Dijkstra\)一招隊優行天下,spfa它已經死了~有負邊仍是要用的……app
#include <cstdio> #include <iostream> #include <queue> using namespace std; const int N = 1e5 + 10; const int M = 2e5 + 10; int n, m, head[N], dis[N], nume, vis[N]; struct tu { int from, to, next, dis; } e[M]; struct node { int po, dis; bool operator<(const node& b) const { return dis > b.dis; } }; void add_edge(int from, int to, int dis) { e[++nume].next = head[from]; e[nume].to = to; e[nume].dis = dis; head[from] = nume; } priority_queue<node> q; void dij(int s) { dis[s] = 0; q.push((node){s, 0}); while (!q.empty()) { node t = q.top(); q.pop(); if (vis[t.po] == 0) { vis[t.po] = 1; for (int i = head[t.po]; i; i = e[i].next) { int to = e[i].to; if (dis[to] > dis[t.po] + e[i].dis) { dis[to] = dis[t.po] + e[i].dis; if (vis[to] == 0) q.push((node){to, dis[to]}); } } } } } int main() { scanf("%d%d", &n, &m); int s; scanf("%d", &s); for (int i = 1; i <= n; i++) dis[i] = 1e9; int u, v, w; for (int i = 1; i <= m; i++) { scanf("%d%d%d", &u, &v, &w); add_edge(u, v, w); } dij(s); for (int i = 1; i <= n; i++) { printf("%d ", dis[i]); } return 0; }
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <string> #define ll long long using namespace std; const int N = 1e6 + 10; const int M = 5e4 + 110; const int inf = 0x3f3f3f3f; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } int T, R, P, S; int head[N], nume; struct Edge { int from, to, net, dis; } e[M << 1]; void add_edge(int from, int to, int dis) { e[++nume] = (Edge){from, to, head[from], dis}; head[from] = nume; } int dis[N]; bool vis[N]; queue<int> q; void spfa(int S) { for (int i = 1; i <= T; i++) dis[i] = inf; dis[S] = 0; q.push(S); vis[S] = 1; while (!q.empty()) { int fr = q.front(); q.pop(); vis[fr] = 0; for (int i = head[fr]; i; i = e[i].net) { int to = e[i].to; if (dis[to] > dis[fr] + e[i].dis) { dis[to] = dis[fr] + e[i].dis; if (!vis[to]) q.push(to), vis[to] = 1; } } } } int main() { T = read(), R = read(), P = read(), S = read(); for (int i = 1, u, v, w; i <= R; i++) { u = read(), v = read(), w = read(); add_edge(u, v, w), add_edge(v, u, w); } for (int i = 1, u, v, w; i <= P; i++) { u = read(), v = read(), w = read(); add_edge(u, v, w); } spfa(S); for (int i = 1; i <= T; i++) { // if(i == S) continue; if (dis[i] == inf) puts("NO PATH"); else printf("%d\n", dis[i]); } system("pause"); return 0; }
\(spfa\)的優化確實是要學一學了學習
孤島營救問題優化
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <string> #define ll long long using namespace std; const int N = 13; const int dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1}; int n, m, e[N][N][N][N], cnt[N][N], key[N][N][N],vis[N][N][(1 << 14)]; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } struct node { int x, y, k, dis; }; int getkey(int x, int y) { int ans = 0; for (int i = 1; i <= cnt[x][y];i++) ans |= (1 << (key[x][y][i] - 1)); return ans; } queue<node> q; int bfs(int sx, int sy) { int sk = getkey(sx, sy); q.push((node){sx, sy, sk, 0}); vis[sx][sy][sk] = 1; while (!q.empty()) { node fr = q.front(); q.pop(); if (fr.x == n && fr.y == m) return fr.dis; for (int i = 0; i < 4; i++) { int tx = fr.x + dx[i], ty = fr.y + dy[i]; int Key = e[fr.x][fr.y][tx][ty]; if (tx < 1 || tx > n || ty < 1 || ty > m || Key < 0 || (Key && !(fr.k&(1<<(Key - 1))))) continue; int Nkey = (fr.k | getkey(tx,ty)); if (vis[tx][ty][Nkey]) continue; q.push((node){tx, ty, Nkey, fr.dis + 1}), vis[tx][ty][Nkey] = 1; } } return -1; } int main() { n = read(), m = read(); int p = read(); int k = read(); for (int i = 1; i <= k; i++) { int x = read(), y = read(), x_ = read(), y_ = read(), g = read(); if (g) e[x][y][x_][y_] = e[x_][y_][x][y] = g; else e[x][y][x_][y_] = e[x_][y_][x][y] = -1; } int s = read(); for (int i = 1; i <= s; i++) { int x = read(), y = read(), q = read(); key[x][y][++cnt[x][y]] = q; } printf("%d", bfs(1, 1)); system("pause"); return 0; }
汽車加油行駛問題spa
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <string> #define ll long long using namespace std; const int N = 110; const int dx[4] = {0, 1, -1, 0}; const int dy[4] = {1, 0, 0, -1}; struct node { int x, y, k, dis; bool operator<(const node& b) const { return dis > b.dis; } }; int n, k, a, b, c; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } bool mapp[N][N], vis[N][N][12]; int dis[N][N][12]; priority_queue<node> q; void dij() { memset(dis, 63, sizeof(dis)); dis[1][1][k] = 0; q.push((node){1, 1, k, 0}); while (!q.empty()) { node tp = q.top(); q.pop(); if (vis[tp.x][tp.y][tp.k]) continue; vis[tp.x][tp.y][tp.k] = 1; for (int i = 0; i <= 3; i++) { node to; to.x = tp.x + dx[i], to.y = tp.y + dy[i], to.k = tp.k - 1, to.dis = tp.dis; if (to.x < 1 || to.x > n || to.y < 1 || to.y > n) continue; if (mapp[to.x][to.y]) to.dis += a, to.k = k; if (i >= 2) to.dis += b; if (!to.k) to.dis += (a + c), to.k = k; if (dis[to.x][to.y][to.k] > to.dis) { dis[to.x][to.y][to.k] = to.dis; if (!vis[to.x][to.y][to.k]) q.push(to); } } } } int main() { n = read(), k = read(), a = read(), b = read(), c = read(); for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) mapp[i][j] = read(); dij(); if (dis[n][n][k] != 0x3f3f3f3f) dis[n][n][0] = min(dis[n][n][0], dis[n][n][k] - a - c); int ans = 0x3f3f3f3f; for (int i = 0; i < k; i++) ans = min(ans, dis[n][n][i]); printf("%d", ans); system("pause"); return 0; }
答案爲 最小 的 路徑上的 最大 的邊權值,顯然知足單調性。
二分答案枚舉 最長的邊權值 \(mid\)
問題轉化爲:僅通過不超過\(k\)條權值 \(>mid\) 的邊,可否找到一條從\(1\)到\(n\)的路徑。
考慮怎麼求得權值 \(>mid\) 的邊最少的, \(1 \to n\)的路徑。
要求路徑上這樣的邊最少,想到用最短路。
考慮建一張新圖:
將 \(>mid\) 的邊的權值變爲 \(1\),代表通過這條邊須要\(1\) 的代價。
將其餘邊的權值其餘變爲 \(0\),代表通過它不須要代價。
轉化後,求得 \(1 \to n\) 新圖中的最短路,即爲上面想要的 \(1 \to n\) 路徑上 \(>mid\) 的邊的最少數量。
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <string> #define ll long long using namespace std; const int N = 2111; const int inf = 0x3f3f3f3f; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } struct Edge { int from, to, dis, net; } e[N << 1]; int head[N], nume; void add_edge(int from, int to, int dis) { e[++nume] = (Edge){from, to, dis, head[from]}; head[from] = nume; } struct node { int po, dis; bool operator<(const node& b) const { return dis > b.dis; } }; int dis[N], vis[N]; int n, p, k; bool check(int lim) { priority_queue<node> q; for (int i = 1; i <= n; i++) dis[i] = inf, vis[i] = 0; dis[1] = 0; q.push((node){1, 0}); while (!q.empty()) { node fr = q.top(); q.pop(); for (int i = head[fr.po]; i; i = e[i].net) { int to = e[i].to; if (dis[to] > dis[fr.po] + (e[i].dis > lim)) { dis[to] = dis[fr.po] + (e[i].dis > lim); if (!vis[to]) q.push((node){to, dis[to]}); } } } return dis[n] <= k; } int main() { n = read(), p = read(), k = read(); int l = 0, r = 0; for (int i = 1, u, v, w; i <= p; i++) { u = read(), v = read(), w = read(); add_edge(u, v, w), add_edge(v, u, w); r = max(r, w); } int ans = -1; while (l <= r) { int mid = (l + r) >> 1; if (check(mid)) { ans = mid; r = mid - 1; } else l = mid + 1; } printf("%d",ans); system("pause"); return 0; }
鴿了半個月的\(Johnson\)全源最短路今天來寫一下筆記
若是我寫的太拉了請點這裏
既然是全源最短路,就不得不考慮\(\text{Floyed}\)可是\(\text{Floyed}\)不能處理負邊,處理負邊考慮的話就是\(\text {spfa}\),那麼就能夠考慮一種跑\(\text n\)輪\(\text {spfa}\)的明顯算法,可是數據會卡\(\text{spfa}\)這就很狗了,那考慮另外一種想有負邊的時候考慮\(\text {spfa}\)沒有負邊的時候跑\(\text {Dijkstra}\),這種想法確實在理論上是可行的的,可是具體的效果怎麼樣確實不知道,效率不會過高
引入另外一種全源最短路的算法的算法 \(\text Johnson\) 全源最短路
首先考慮怎麼處理負邊與負環,\(\text{spfa}\)雖然它死了可是我確實不知道有什麼其餘的算法能對這進行處理,對於\(\text{spfa}\)負環處理很簡單,對於每一個點的進隊次數進行統計若是這個點的進隊次數達到了全部點的個數,就能夠判斷這個圖存在負環關於負環的判斷學習能夠往下翻,下面有幾道例題
首先考慮\(\text{spfa}\)中的\(\text{dis}\)數組,那麼咱們考慮兩個點之間的距離是不是必定的,答案很顯然,在一張圖中任意的兩點之間的最短距離是肯定的,即便有另外一條吧邊和最短的那條邊相等,可是對答案依舊造不成任何的影響,那就考慮用這個\(\text{dis}\)數組中存的值進行最短路,能夠獲得一個很顯然的結論
對每一個點與虛點連一條邊,從虛點跑一遍\(\text{spfa}\),虛點到每一個點的最短路爲\(\text h_i\),對於每兩個點之間的邊權從新定義:
\(u\to v\)之間有一條邊權爲\(\text w\),從新定義爲\(w + h_u - h_v\)
本身怎麼寫也感受本身寫的很差
從上面的式子能夠看出\(h_s - h_t\)的值是固定不變的,對於新的邊權的圖進行跑\(\text{Dijkstra}\)跑\(\text n\)輪便可
notice:
有一個很大的坑點,就是由於咱們建了一個虛點,因此跑\(spfa\)時一個點進入大於\(n\)次時才能判斷出現負環了
模板
code
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <string> #define int long long using namespace std; const int N = 3e3 + 100; const int M = 6e3 + 100; const int inf = 1e9; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } int n, m; struct node { int po, dis; bool operator < (const node& b) const { return dis > b.dis; } }; struct Edge { int from, to, dis, net; } e[M << 1]; int nume, head[N]; void add_edge(int from, int to, int dis) { e[++nume] = (Edge){from, to, dis, head[from]}; head[from] = nume; } int h[N], cnt[N]; bool vis[N]; bool spfa(int s) { memset(h,0x3f,sizeof(h)); memset(vis,0,sizeof(vis)); queue<int> Q; h[s] = 0; Q.push(s); vis[s] = 1; cnt[s] = 1; while (!Q.empty()) { int fr = Q.front(); Q.pop(); vis[fr] = 0; for (int i = head[fr]; i; i = e[i].net) { int to = e[i].to; if (h[to] > h[fr] + e[i].dis) { h[to] = h[fr] + e[i].dis; if (!vis[to]) { Q.push(to);vis[to] = 1; cnt[to]++; if (cnt[to] > n) return true; } } } } return false; } int dis[N]; void dij(int s) { priority_queue <node> q; for(int i = 1; i <= n; i++) dis[i] = inf, vis[i] = 0; q.push((node){s, 0}); dis[s] = 0; while (!q.empty()) { node tp = q.top(); q.pop(); if (vis[tp.po]) continue; vis[tp.po] = 1; for (int i = head[tp.po]; i; i = e[i].net) { int to = e[i].to; if (dis[to] > dis[tp.po] + e[i].dis) { dis[to] = dis[tp.po] + e[i].dis; if (!vis[to]) { q.push((node){to,dis[to]}); } } } } } signed main() { n = read(), m = read(); for (int i = 1, u, v, w; i <= m; i++) { u = read(), v = read(), w = read(); add_edge(u, v, w); } for (int i = 1; i <= n; i++) { add_edge(0, i, 0); } if (spfa(0)) { puts("-1"); system("pause"); return 0; } else { for (int i = 1; i <= n; i++) { for (int j = head[i]; j; j = e[j].net) { e[j].dis += h[i] - h[e[j].to]; } } for (int i = 1; i <= n; i++) { dij(i); int ans = 0; for (int j = 1; j <= n; j++) { if (dis[j] == inf) ans += j * inf; else ans += j * (dis[j] + h[j] - h[i]); } printf("%lld\n", ans); } } system("pause"); return 0; }
這個確實沒啥能夠說的,有點題目就是坑點就是它的圖不聯通,就噁心你,根據\(spfa\)的原理更新了以後就會入隊,那一個點反覆入隊不就說明存在負環了嗎
模板題
code
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <string> #include <queue> #define ll long long using namespace std; const int N = 505; const int inf = 1e9; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } struct Edge { int from, to, dis, net; } e[N * N]; int nume, head[N]; void add_edge(int from, int to, int dis) { e[++nume] = (Edge){from, to, dis, head[from]}; head[from] = nume; } int n, m, w; queue<int> q; int dis[N],cnt[N]; bool vis[N]; bool spfa() { q.push(1); vis[1] = 1;cnt[1] = 1;dis[1] = 0; while (!q.empty()) { int fr = q.front(); q.pop(); vis[fr] = 0; for (int i = head[fr]; i; i = e[i].net) { int to = e[i].to; if (dis[to] > dis[fr] + e[i].dis) { dis[to] = dis[fr] + e[i].dis; if (!vis[to]) { cnt[to]++; q.push(to), vis[to] = 1; if(cnt[to] >= n) return true; } } } } return false; } void clear() { for(int i = 1 ; i <= nume ;i++) { e[i].from = e[i].to = e[i].dis = e[i].net = 0; } nume = 0; memset(head,0,sizeof(head)); memset(vis,0,sizeof(vis)); memset(dis,0x3f,sizeof(dis)); memset(cnt,0,sizeof(cnt)); } int main() { int T = read(); while(T--) { clear(); n = read(), m = read(), w = read(); for (int i = 1, u, v, w_; i <= m; i++) { u = read(), v = read(), w_ = read(); add_edge(u, v, w_), add_edge(v, u, w_); } for (int i = 1, u, v, w_; i <= w; i++) { u = read(), v = read(), w_ = read(); add_edge(u, v, -w_); } if(spfa()) puts("YES"); else puts("NO"); } system("pause"); return 0; }
噁心的題
這個題同樣的裸的spfa可是,它的圖不聯通,能夠考慮rand()%n隨機一下hhhh
哈希隨機模數,隨機得分,玄學
#include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <queue> #include <string> #include <ctime> #define int long long using namespace std; const int N = 1e3+100; const int M = 1e5+100; const int inf = 0x7f7f7f7f; int read() { int s = 0, f = 0; char ch = getchar(); while (!isdigit(ch)) f |= (ch == '-'), ch = getchar(); while (isdigit(ch)) s = s * 10 + (ch ^ 48), ch = getchar(); return f ? -s : s; } int n, m; struct Edge { int from, to, dis, net; } e[M << 1]; int head[N], nume; void add_edge(int from, int to, int dis) { e[++nume] = (Edge) { from, to, dis, head[from] }; head[from] = nume; } queue<int> q; bool vis[N]; int dis[N], cnt[N]; bool spfa(int s) { for (int i = 1; i <= n; i++) dis[i] = inf,vis[i] = 0; srand(time(0)); q.push(s); vis[s] = 1; dis[s] = 0; cnt[s] = 1; while (!q.empty()) { int fr = q.front(); q.pop(), vis[fr] = 0; for (int i = head[fr]; i; i = e[i].net) { int to = e[i].to; if (dis[to] > dis[fr] + e[i].dis) { dis[to] = dis[fr] + e[i].dis; if (!vis[to]) { cnt[to]++; q.push(to), vis[to] = 1; if (cnt[to] >= n) return true; } } } } return false; } signed main() { // freopen("test3.in","r",stdin) ; // freopen("test3.out","w",stdout) ; n = read(), m = read();int s = read(); for (int i = 1, u, v, w; i <= m; i++) { u = read(), v = read(), w = read(); add_edge(u, v, w); } if (spfa(n/2)||spfa(s)) { puts("-1"); } else { dis[s] = 0; for (int i = 1; i <= n; i++) { if(dis[i] == inf) puts("NoPath"); else printf("%lld\n", dis[i]); } } system("pause"); return 0; }