最短路

最短路

多源最短路

\(Floyed\)三重循環暴力處理node

Floyed經典題ios

  • 貌似這題跑一遍Floyed 比跑 \(N\)遍dij都快

題目有億點難壓縮
solution :
image.png
出題人良心,看到這個條件能夠考慮\(FLoyed\)git

  1. 能夠指定一個指針,把小於當前詢問天數的最短路跑完
  2. 若是詢問的兩點已經聯通,就出最短路徑,不然輸出(-1)
    code
#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]);
  }
}

無向圖的最小環問題算法

  • 也是用\(Floyed\)來進行求解
  • 根據鬆弛操做\(dis[u][v] = min(dis[u][k] + dis[k][v],dis[u][v])\)
  • 能夠很顯然的獲得求最小環的一種操做

\[ans = min(dis[i][j],dis[i][j] + e[i][k] + e[k][j]) \]

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

求最小環的路徑網絡

  • 根據上一個求小環的過程,能夠一樣的處理,能夠開一個\(vector\)或者一個pre數組記錄路徑,每次更新最小環的時候將之清空
  • 求路徑的過程能夠經過搜索來實現
  • 這個題的數據有點假,不知道是什麼緣由邊長開\(long~long\)會讓這個東西掛掉
    code
#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

dij 板子

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

spfa 板子

#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\)的優化確實是要學一學了學習

  • 優化1:SLF
  • 優化2:LLL
  • 優化3:兩點最短路優化
    我都不會=^=,嘎嘎,必定學

網絡流能夠用最短路求解的問題

孤島營救問題優化

  • 典型的矩陣最短路問題,可是對於矩陣的最短路求解其實相似於BFS,由於每一代價都爲1,這就保證了第一次到底終點(搜到答案)獲得的最短路就是答案,正解就是狀壓下的廣搜,鑰匙並非用完以後就不能用了,由於鑰匙的種類最多有14種,能夠考慮用二進制位來處理,當這一位爲1時表示有第幾種鑰匙
  • 好像還有另外一種寫法是用鑰匙分層圖跑最短路
    notice :
  1. 鑰匙不是用了就沒了
  2. 一個點能夠放多個鑰匙
  3. 初始點能夠放鑰匙
  4. 別忘了輸出-1
#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

  • 考慮將問題轉化,會發現,能夠根據當前的油量構造建成一個分層圖最短路的模型可是不構建分層圖能夠給\(dis[x][y][x]\) 數組多開一維表示到座標\((x,y)\)的油量爲\(x\)的最小花費
    notice:
  1. 若汽車到達一個有加油站的點,那麼必須將油加滿爲\(k\),花費爲\(a\);
  2. 若汽車往回走,即前往的點的橫座標或者縱座標在減少,須要花費\(b\);
  3. 汽車也能夠本身新建一個加油站,花費\(c\)(不包括加油費用\(a\))可是隻要有加油站就要加油因此創建了加油站主要這一點同時若是到達終點沒有油的話是不須要創建加油站且不須要
  4. 起點可有加油站
#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 全源最短路

  • 鴿了半個月的\(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\)
    image.png
    本身怎麼寫也感受本身寫的很差

從上面的式子能夠看出\(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判負環

這個確實沒啥能夠說的,有點題目就是坑點就是它的圖不聯通,就噁心你,根據\(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;
}
相關文章
相關標籤/搜索