最短路

多源最短路

\(Floyed\)三重循环暴力处理

Floyed经典题

  • 貌似这题跑一遍Floyed 比跑 \(N\)遍dij都快

题目有亿点难压缩
solution :

出题人良心,看到这个条件可以考虑\(FLoyed\)

  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它已经死了~有负边还是要用的……

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

汽车加油行驶问题

  • 考虑将问题转化,会发现,可以根据当前的油量构造建成一个分层图最短路的模型但是不构建分层图可以给\(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\)

    自己怎么写也感觉自己写的不好

从上面的式子可以看出\(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;
}
posted @ 2021-01-20 22:01  Imy_bisLy  阅读(107)  评论(0编辑  收藏  举报