【7.89%】【BNUOJ 52303】Floyd-Warshall
来源:互联网 发布:淘宝防狼神器 编辑:程序博客网 时间:2024/06/17 00:54
Time limit: 2 seconds
Memory limit: 1024 megabytes
In ICPCCamp, there are n cities and m (bidirectional) roads between cities. The i-th road is between the
ai-th city and the bi-th city. There may be roads connecting a citie to itself and multiple roads between
the same pair of cities.
Bobo has q travel plans. The i-th plan is to travel from the ui-th city to the vi-th city. He would like
to know the smallest number of roads needed to travel for each plan. It is guaranteed that cities are
connected.
【Input】
The first line contains 3 integers n, m, q (1 ≤ n ≤ 10^5, 0 < m − n < 100, 1 ≤ q ≤ 10^5).
The i-th of the following m lines contains 2 integers ai, bi (1 ≤ ai, bi ≤ n).
The i-th of the last q lines contains 2 integers ui, vi (1 ≤ ui, vi ≤ n)
【Output】
n lines with integers l1, l2, … , ln. li denotes the smallest number of roads travelling from city ui to city vi.
Examples
【standard input 1】
4 5 3
1 2
1 3
1 4
2 3
3 4
2 2
2 3
2 4
【standard output 1】
0
1
2
【standard input 1】
1 2 1
1 1
1 1
1 1
【standard output 1】
0
【题解】
毒题。
讲一下做法:
注意到m最多为n+100最小为n
我们先在m条边中取出n-1条。让它组成一棵树。(用并查集去除多余的边,但是这些多余的边不能删掉要记录下来);
对于每一个询问x,y;先求出它们的最近公共祖先z;然后设某个节点x的深度为dep[x]
则两个点之间的最短路一定是dep[x]+dep[y]-2*dep[z];
但是这只是在去掉了多余的边后的情况。
我们还要把那些边再加进来。
全部都加进来之后。
从加进来的多余的边的端点开始进行spfa。
设询问仍然是x,y;
则再尝试用dis[s][x]+dis[s][y]来更新答案。其中s∈{x|x为多余边的端点}
这里的dis第一维只要开到200多就可以了。
相当于我们在一棵树上进行了一次最短路。
然后再用多余的边上的点作为起点进行最短路。
如果那些多余边上的点是最短路上的点。那么在第二次更新dis[s][x]+dis[s][y]的时候都会涉及到。
可以理解为x->y经过了s这个点的最短路。枚举s。取最小。
添加了边。最短路发生改变。那么新的最短路肯定经过某一条新添加的边的两端点。
很棒的思路。
最后是LCA的实现思路:
盗了几张图:
我发两份代码:
第一份用的是RMQ预处理出LCA的方法。可以AC;
第二份用的是树上倍增的方法,但是TLE。(供学习);
//代码一,可AC#include <cstdio>#include <iostream>#include <queue>#include <algorithm>#include <vector>using namespace std;const int MAXN = 4e5 + 10;const int MAX_REST = 100 + 10;const int INF = 2100000000;int n, m, q, ans[MAXN], dep[MAXN] = { 0 };int pr[25];queue <int> dl;int dis[MAX_REST * 2][MAXN];struct bian{ int x, y;};vector <int> a[MAXN], c;vector < pair<int, int> > rest, b;int f[MAXN], re = 0;int p[MAXN][25];int fi[MAXN], cnt = 0;int shall[MAXN * 2];void input(int &num){ num = 0; char c; do { c = getchar(); } while (!isdigit(c)); while (isdigit(c)) { num = num * 10 + c - '0'; c = getchar(); }}int ff(int x){ if (f[x] == x) return f[x]; else f[x] = ff(f[x]); return f[x];}int lca(int x, int y){ int xx = fi[x]; int yy = fi[y]; if (xx > yy) swap(xx, yy); int k = shall[yy - xx + 1]; int a1 = p[xx][k]; int a2 = p[yy - pr[k] + 1][k];//左边和右边都搞下才能覆盖整个区间 if (dep[a1] < dep[a2]) return a1; else return a2;}void dfs(int u, int fa){ dep[u] = dep[fa] + 1; p[++cnt][0] = u;//dfs序列为cnt,长度为2^0的深度最小的节点 fi[u] = cnt; int len = a[u].size(); for (int i = 0; i <= len - 1; i++) { int y = a[u][i]; if (y != fa) { dfs(y, u); p[++cnt][0] = u; } }}void spfa(int s){ for (int i = 1; i <= n; i++)//用memset会比较慢 dis[s][i] = INF; dis[s][c[s]] = 0; dl.push(c[s]); while (!dl.empty()) { int x = dl.front(); dl.pop(); int len = a[x].size(); for (int i = 0; i <= len - 1; i++) { int y = a[x][i]; if (dis[s][y] >dis[s][x] + 1) { dis[s][y] = dis[s][x] + 1; dl.push(y); } } }}int main(){ //freopen("F:\\rush.txt", "r", stdin); input(n); input(m); input(q); for (int i = 1; i <= n; i++) f[i] = i; for (int i = 1; i <= m; i++) { int x, y; input(x); input(y); if (x == y) continue; int r1 = ff(x), r2 = ff(y); if (r1 != r2) { f[r1] = r2; a[x].push_back(y); a[y].push_back(x); } else rest.push_back(make_pair(x, y)); } dfs(1, 0); pr[0] = 1; for (int i = 1; i <= 24; i++) pr[i] = pr[i - 1] << 1; shall[1] = 0; int now = 1; for (int i = 2; i <= cnt; i++)//shall[i]表示长度为i的区间需要2的多少次方来覆盖。 { if (i == pr[now]) { shall[i] = shall[i - 1] + 1; now++; } else shall[i] = shall[i - 1]; } for (int i = 1; pr[i] <= cnt; i++) for (int j = 1; j + pr[i] - 1 <= cnt; j++)//用RMQ来做 { int x = p[j][i - 1]; int y = p[j + pr[i - 1]][i - 1]; if (dep[x] < dep[y]) p[j][i] = x; else p[j][i] = y; } for (int i = 1; i <= q; i++) { int x, y; input(x); input(y); b.push_back(make_pair(x, y)); ans[i] = dep[x] + dep[y] - 2 * dep[lca(x, y)]; } int len = rest.size();//把多余的边加入图中 for (int i = 0; i <= len - 1; i++) { int x = rest[i].first, y = rest[i].second; a[x].push_back(y); a[y].push_back(x); c.push_back(x); c.push_back(y);//并取出边的两端点。用来作为spfa的起点 } len = c.size(); for (int i = 0; i <= len - 1; i++) spfa(i); for (int i = 0; i <= q - 1; i++) { int x, y; x = b[i].first, y = b[i].second; for (int j = 0; j <= len - 1; j++)//需要更新的肯定是那些新添加的点。 { int s = j; ans[i + 1] = min(ans[i + 1], dis[s][x] + dis[s][y]); } printf("%d\n", ans[i + 1]); } return 0;}
//代码二 会超时 树上倍增//递推公式:p[i][j] = p[p[i][j-1][j-1];//p[i][j]表示从i节点往上走2^j个节点后是什么节点。#include <cstdio>#include <queue>#include <cstring>#include <algorithm>#include <vector>using namespace std;const int MAXN = 4e5;const int MAX_REST = 100 + 10;int n, m, q, ans[MAXN], dep[MAXN] = { 0 };int pr[20];queue <int> dl;int dis[MAX_REST*2][MAXN];struct bian{ int x, y;};vector <int> a[MAXN],c;vector < pair<int, int> > rest,b;int f[MAXN],re = 0,p[MAXN][20];int ff(int x){ if (f[x] == x) return f[x]; else f[x] = ff(f[x]); return f[x];}int lca(int x, int y)//倍增求LCA{ if (dep[x] > dep[y]) swap(x, y); for (int i = 17; i >= 0; i--) if (dep[x] <= dep[y] - (pr[i])) y = p[y][i]; if (x == y)//如果x直接等于y了要直接返回; return x; for (int i = 17; i >= 0; i--) { if (p[x][i] == p[y][i]) continue; x = p[x][i], y = p[y][i]; } return p[x][0];}void dfs(int u, int fa){ dep[u] = dep[fa] + 1; p[u][0] = fa; for (int i = 1; i <= 17; i++) p[u][i] = p[p[u][i - 1]][i - 1]; int len = a[u].size(); for (int i = 0;i <= len-1;i++) { int y = a[u][i]; if (y != fa) dfs(y, u); }}void spfa(int s){ memset(dis[s], 127 / 3, sizeof(dis[s])); dis[s][c[s]] = 0; dl.push(c[s]); while (!dl.empty()) { int x = dl.front(); dl.pop(); int len = a[x].size(); for (int i = 0; i <= len - 1; i++) { int y = a[x][i]; if (dis[s][y] > dis[s][x] + 1) { dis[s][y] = dis[s][x] + 1; dl.push(y); } } }}int main(){ //freopen("F:\\rush.txt", "r", stdin); scanf("%d%d%d", &n, &m, &q); for (int i = 1; i <= n; i++) f[i] = i; for (int i = 1; i <= m; i++) { int x, y; scanf("%d%d", &x, &y); if (x == y) continue; int r1 = ff(x), r2 = ff(y); if (r1 != r2) { f[r1] = r2; a[x].push_back(y); a[y].push_back(x); } else rest.push_back(make_pair(x, y)); } dfs(1, 0); pr[0] = 1; for (int i = 1; i <= 17; i++) pr[i] = pr[i - 1] << 1; for (int i = 1; i <= q; i++) { int x, y; scanf("%d%d", &x, &y); b.push_back(make_pair(x, y)); ans[i] = dep[x] + dep[y] - 2 * dep[lca(x, y)]; } int len = rest.size(); for (int i = 0; i <= len - 1; i++) { int x = rest[i].first, y = rest[i].second; a[x].push_back(y); a[y].push_back(x); c.push_back(x); c.push_back(y); } len = c.size(); for (int i = 0; i <= len - 1; i++) spfa(i); for (int i = 0; i <= q-1; i++) { int x, y; x = b[i].first, y = b[i].second; for (int j = 0; j <= len - 1; j++) { int s = j; ans[i+1] = min(ans[i+1], dis[s][x] + dis[s][y]); } printf("%d\n", ans[i+1]); } return 0;}
- 【7.89%】【BNUOJ 52303】Floyd-Warshall
- Floyd-Warshall
- Floyd-Warshall
- Floyd-Warshall
- floyd-warshall
- Floyd-Warshall
- Floyd-Warshall算法(Floyd-Warshall algorithm)
- Floyd-Warshall多源最短路径
- Floyd-Warshall 算法
- Floyd-Warshall算法
- poj1125 Floyd-Warshall算法
- Floyd-Warshall算法详解
- Floyd-Warshall算法
- Floyd-Warshall算法
- Floyd-Warshall Algorithm
- floyd—warshall
- Floyd-Warshall算法
- Floyd-Warshall算法
- cf 721 d(优先队列+贪心)
- 简单的mvc composer加载medoo数据库类
- caffe+python 使用训练好的VGG16模型 对 单张图片进行分类,输出置信度
- 关于dubbo使用过程中抛出【java.util.concurrent.RejectedExecutionException: Thread pool is EXHAUSTED!】的思考
- 如何对二次元控件进行数值运算
- 【7.89%】【BNUOJ 52303】Floyd-Warshall
- 华硕笔记本从win10改成win7后,USB3.0无法使用
- 理解Java对象序列化
- 顺序表应用7:最大子段和之分治递归法
- 数据类型
- 一个自定义的修改用户名的dialog
- 数据结构:从堆到“漏斗”
- Linux下静态链接库与动态链接库的区别
- 基于Eclipse的spring开发环境搭建