【模板】Floyd
题目描述
给出一张由 n n n 个点 m m m 条边组成的无向图。
求出所有点对 ( i , j ) (i,j) (i,j) 之间的最短路径。
输入格式
第一行为两个整数 n , m n,m n,m,分别代表点的个数和边的条数。
接下来 m m m 行,每行三个整数 u , v , w u,v,w u,v,w,代表 u , v u,v u,v 之间存在一条边权为 w w w 的边。
输出格式
输出 n n n 行每行 n n n 个整数。
第 i i i 行的第 j j j 个整数代表从 i i i 到 j j j 的最短路径。
样例 #1
样例输入 #1
4 4
1 2 1
2 3 1
3 4 1
4 1 1
样例输出 #1
0 1 2 1
1 0 1 2
2 1 0 1
1 2 1 0
提示
对于 100 % 100\% 100% 的数据, n ≤ 100 n \le 100 n≤100, m ≤ 4500 m \le 4500 m≤4500,任意一条边的权值 w w w 是正整数且 1 ⩽ w ⩽ 1000 1 \leqslant w \leqslant 1000 1⩽w⩽1000。
思路:见标题
代码
#include <iostream>
#include <vector>
using namespace std;
const int INF = 1e9;
int main() {
int n, m;
cin >> n >> m;
vector<vector<int>> graph(n, vector<int>(n, INF));
for (int i = 0; i < n; ++i) {
graph[i][i] = 0;
}
for (int i = 0; i < m; ++i) {
int u, v, w;
cin >> u >> v >> w;
graph[u - 1][v - 1] = w;
graph[v - 1][u - 1] = w;
}
for (int k = 0; k < n; ++k) {
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
graph[i][j] = min(graph[i][j], graph[i][k] + graph[k][j]);
}
}
}
for (int i = 0; i < n; ++i) {
for (int j = 0; j < n; ++j) {
if (graph[i][j] == INF) {
cout << -1 << " ";
} else {
cout << graph[i][j] << " ";
}
}
cout << endl;
}
return 0;
}
【模板】单源最短路径(标准版)
题目背景
2018 年 7 月 19 日,某位同学在 NOI Day 1 T1 归程 一题里非常熟练地使用了一个广为人知的算法求最短路。
然后呢?
100 → 60 100 \rightarrow 60 100→60;
Ag → Cu \text{Ag} \rightarrow \text{Cu} Ag→Cu;
最终,他因此没能与理想的大学达成契约。
小 F 衷心祝愿大家不再重蹈覆辙。
题目描述
给定一个 n n n 个点, m m m 条有向边的带非负权图,请你计算从 s s s 出发,到每个点的距离。
数据保证你能从 s s s 出发到任意点。
输入格式
第一行为三个正整数
n
,
m
,
s
n, m, s
n,m,s。
第二行起
m
m
m 行,每行三个非负整数
u
i
,
v
i
,
w
i
u_i, v_i, w_i
ui,vi,wi,表示从
u
i
u_i
ui 到
v
i
v_i
vi 有一条权值为
w
i
w_i
wi 的有向边。
输出格式
输出一行 n n n 个空格分隔的非负整数,表示 s s s 到每个点的距离。
样例 #1
样例输入 #1
4 6 1
1 2 2
2 3 2
2 4 1
1 3 5
3 4 3
1 4 4
样例输出 #1
0 2 4 3
提示
样例解释请参考 数据随机的模板题。
1 ≤ n ≤ 1 0 5 1 \leq n \leq 10^5 1≤n≤105;
1 ≤ m ≤ 2 × 1 0 5 1 \leq m \leq 2\times 10^5 1≤m≤2×105;
s = 1 s = 1 s=1;
1 ≤ u i , v i ≤ n 1 \leq u_i, v_i\leq n 1≤ui,vi≤n;
0 ≤ w i ≤ 1 0 9 0 \leq w_i \leq 10 ^ 9 0≤wi≤109,
0 ≤ ∑ w i ≤ 1 0 9 0 \leq \sum w_i \leq 10 ^ 9 0≤∑wi≤109。
本题数据可能会持续更新,但不会重测,望周知。
2018.09.04 数据更新 from @zzq
思路:Dijkstra 算法的变体
代码
#include<bits/stdc++.h>
#define M(x,y) make_pair(x,y)
using namespace std;
int fr[100010],to[200010],nex[200010],v[200010],tl,d[100010];
bool b[100010];
void add(int x,int y,int w){
to[++tl]=y;
v[tl]=w;
nex[tl]=fr[x];
fr[x]=tl;
}
priority_queue< pair<int,int> > q;
int main(){
int n,m,x,y,z,s;
scanf("%d%d%d",&n,&m,&s);
for(int i=1;i<=m;i++){
scanf("%d%d%d",&x,&y,&z);
add(x,y,z);
}
for(int i=1;i<=n;i++) d[i]=1e10;
d[s]=0;
q.push(M(0,s));
while(!q.empty()){
int x=q.top().second;
q.pop();
if(b[x]) continue;
b[x]=1;
for(int i=fr[x];i;i=nex[i]){
int y=to[i],l=v[i];
if(d[y]>d[x]+l){
d[y]=d[x]+l;
q.push(M(-d[y],y));
}
}
}
for(int i=1;i<=n;i++) printf("%d ",d[i]);
return 0;
}
[NOIP2015 提高组] 信息传递
题目背景
NOIP2015 Day1T2
题目描述
有 n n n 个同学(编号为 1 1 1 到 n n n)正在玩一个信息传递的游戏。在游戏里每人都有一个固定的信息传递对象,其中,编号为 i i i 的同学的信息传递对象是编号为 T i T_i Ti 的同学。
游戏开始时,每人都只知道自己的生日。之后每一轮中,所有人会同时将自己当前所知的生日信息告诉各自的信息传递对象(注意:可能有人可以从若干人那里获取信息,但是每人只会把信息告诉一个人,即自己的信息传递对象)。当有人从别人口中得知自己的生日时,游戏结束。请问该游戏一共可以进行几轮?
输入格式
输入共 2 2 2 行。
第一行包含 1 1 1 个正整数 n n n,表示 n n n 个人。
第二行包含 n n n 个用空格隔开的正整数 T 1 , T 2 , ⋯ , T n T_1,T_2,\cdots,T_n T1,T2,⋯,Tn,其中第 i i i 个整数 T i T_i Ti 表示编号为 i i i 的同学的信息传递对象是编号为 T i T_i Ti 的同学, T i ≤ n T_i\leq n Ti≤n 且 T i ≠ i T_i\neq i Ti=i。
输出格式
共一行一个整数,表示游戏一共可以进行多少轮。
样例 #1
样例输入 #1
5
2 4 2 3 1
样例输出 #1
3
提示
样例 1 解释:
游戏的流程如图所示。当进行完第 3 3 3 轮游戏后, 4 4 4 号玩家会听到 2 2 2 号玩家告诉他自己的生日,所以答案为 3 3 3。当然,第 3 3 3 轮游戏后, 2 2 2 号玩家、 3 3 3 号玩家都能从自己的消息来源得知自己的生日,同样符合游戏结束的条件。
- 对于 30 % 30\% 30% 的数据, n ≤ 200 n\le 200 n≤200;
- 对于 60 % 60\% 60% 的数据, n ≤ 2500 n\le 2500 n≤2500;
- 对于 100 % 100\% 100% 的数据, n ≤ 2 × 1 0 5 n\le 2\times 10^5 n≤2×105。
思路:用并查集找环的大小,然后设置一个变量进行全局比较,找到最小的那个即可
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m, fa[N], ans = 0x3f3f3f3f;
int get(int x, int &cnt)
{
cnt++;
if(fa[x] == x)return x;
else return get(fa[x], cnt);
}//找环的长度
int main()
{
cin >> n;
for (int i = 1; i <= n; i++)
fa[i] = i;
for(int i = 1; i <= n; i++)
{
int f, cnt = 0;
cin >> f;
if(get(f, cnt) == i)
{
ans = min(ans, cnt);
}
else
fa[i] = f;
}
cout << ans;
return 0;
}
最短路计数
题目描述
给出一个 N N N 个顶点 M M M 条边的无向无权图,顶点编号为 1 ∼ N 1\sim N 1∼N。问从顶点 1 1 1 开始,到其他每个点的最短路有几条。
输入格式
第一行包含 2 2 2 个正整数 N , M N,M N,M,为图的顶点数与边数。
接下来 M M M 行,每行 2 2 2 个正整数 x , y x,y x,y,表示有一条由顶点 x x x 连向顶点 y y y 的边,请注意可能有自环与重边。
输出格式
共 N N N 行,每行一个非负整数,第 i i i 行输出从顶点 1 1 1 到顶点 i i i 有多少条不同的最短路,由于答案有可能会很大,你只需要输出 $ ans \bmod 100003$ 后的结果即可。如果无法到达顶点 i i i 则输出 0 0 0。
样例 #1
样例输入 #1
5 7
1 2
1 3
2 4
3 4
2 3
4 5
4 5
样例输出 #1
1
1
1
2
4
提示
1 1 1 到 5 5 5 的最短路有 4 4 4 条,分别为 2 2 2 条 1 → 2 → 4 → 5 1\to 2\to 4\to 5 1→2→4→5 和 2 2 2 条 1 → 3 → 4 → 5 1\to 3\to 4\to 5 1→3→4→5(由于 4 → 5 4\to 5 4→5 的边有 2 2 2 条)。
对于
20
%
20\%
20% 的数据,
1
≤
N
≤
100
1\le N \le 100
1≤N≤100;
对于
60
%
60\%
60% 的数据,
1
≤
N
≤
1
0
3
1\le N \le 10^3
1≤N≤103;
对于
100
%
100\%
100% 的数据,
1
≤
N
≤
1
0
6
1\le N\le10^6
1≤N≤106,
1
≤
M
≤
2
×
1
0
6
1\le M\le 2\times 10^6
1≤M≤2×106。
思路:由于题目说考虑第一个点到第i个点的距离,而且无边权,那我们就可以考虑bfs
代码
#include<bits/stdc++.h>
using namespace std;
const int N = 2e6 + 10, MOD = 1e5 + 3, INF = 0x7f7f7f7f;
vector<int> G[N];
int dep[N], cnt[N];//dep记录到1的距离
bool vis[N];//标记是否走过
int a[N], n, m;
int main()
{
cin >> n >> m;
for (int i = 1; i <= m; i++)
{
int x, y;
cin >> x >> y;
G[x].push_back(y);
G[y].push_back(x);
}
queue<int> qu;
dep[1] = 0;
vis[1] = 1;
qu.push(1);
cnt[1] = 1;
while (!qu.empty())
{
int x = qu.front();
qu.pop();
for (int i = 0; i < G[x].size(); i++)
{
int t = G[x][i];
if (!vis[t])
{
vis[t] = 1;
dep[t] = dep[x] + 1;
qu.push(t);
}
if (dep[t] == dep[x] + 1)
{
cnt[t] = (cnt[t] + cnt[x]) % MOD;
}
}
}
for (int i = 1; i <= n; i++)
{
cout << cnt[i] << endl;
}
return 0;
}
[蓝桥杯 2022 国 A] 环境治理
题目描述
LQ 国拥有 n n n 个城市,从 0 0 0 到 n − 1 n - 1 n−1 编号,这 n n n 个城市两两之间都有且仅有一条双向道路连接,这意味着任意两个城市之间都是可达的。每条道路都有一个属性 D D D,表示这条道路的灰尘度。当从一个城市 A 前往另一个城市 B 时,可能存在多条路线,每条路线的灰尘度定义为这条路线所经过的所有道路的灰尘度之和,LQ 国的人都很讨厌灰尘,所以他们总会优先选择灰尘度最小的路线。
LQ 国很看重居民的出行环境,他们用一个指标 P P P 来衡量 LQ 国的出行环境, P P P 定义为:
P = ∑ i = 0 n − 1 ∑ j = 0 n − 1 d ( i , j ) P=\sum \limits_{i=0}^{n-1} \sum \limits_{j=0}^{n-1} d(i,j) P=i=0∑n−1j=0∑n−1d(i,j)
其中 d ( i , j ) d(i,j) d(i,j) 表示城市 i i i 到城市 j j j 之间灰尘度最小的路线对应的灰尘度的值。
为了改善出行环境,每个城市都要有所作为,当某个城市进行道路改善时,会将与这个城市直接相连的所有道路的灰尘度都减少 1 1 1,但每条道路都有一个灰尘度的下限值 L L L,当灰尘度达到道路的下限值时,无论再怎么改善,道路的灰尘度也不会再减小了。
具体的计划是这样的:
- 第 1 1 1 天, 0 0 0 号城市对与其直接相连的道路环境进行改善;
- 第 2 2 2 天, 1 1 1 号城市对与其直接相连的道路环境进行改善;
……
- 第 n n n 天, n − 1 n - 1 n−1 号城市对与其直接相连的道路环境进行改善;
- 第 n + 1 n + 1 n+1 天, 0 0 0 号城市对与其直接相连的道路环境进行改善;
- 第 n + 2 n + 2 n+2 天, 1 1 1 号城市对与其直接相连的道路环境进行改善;
……
LQ 国想要使得 P P P 指标满足 P ≤ Q P \leq Q P≤Q。请问最少要经过多少天之后, P P P 指标可以满足 P ≤ Q P \leq Q P≤Q。如果在初始时就已经满足条件,则输出 0 0 0;如果永远不可能满足,则输出 − 1 -1 −1。
输入格式
输入的第一行包含两个整数 n , Q n, Q n,Q,用一个空格分隔,分别表示城市个数和期望达到的 P P P 指标。
接下来 n n n 行,每行包含 n n n 个整数,相邻两个整数之间用一个空格分隔,其中第 i i i 行第 j j j 列的值 D i , j ( D i , j = D j , i , D i , i = 0 ) D_{i,j} (D_{i,j}=D_{j,i},D_{i,i} = 0) Di,j(Di,j=Dj,i,Di,i=0) 表示城市 i i i 与城市 j j j 之间直接相连的那条道路的灰尘度。
接下来 n n n 行,每行包含 n n n 个整数,相邻两个整数之间用一个空格分隔,其中第 i i i 行第 j j j 列的值 L i , j ( L i , j = L j , i , L i , i = 0 ) L_{i,j} (L_{i,j} = L_{j,i}, L_{i,i} = 0) Li,j(Li,j=Lj,i,Li,i=0) 表示城市 i i i 与城市 j j j 之间直接相连的那条道路的灰尘度的下限值。
输出格式
输出一行包含一个整数表示答案。
样例 #1
样例输入 #1
3 10
0 2 4
2 0 1
4 1 0
0 2 2
2 0 0
2 0 0
样例输出 #1
2
提示
【样例说明】
初始时的图如下所示,每条边上的数字表示这条道路的灰尘度:
此时每对顶点之间的灰尘度最小的路线对应的灰尘度为:
- d ( 0 , 0 ) = 0 , d ( 0 , 1 ) = 2 , d ( 0 , 2 ) = 3 d(0, 0) = 0, d(0, 1) = 2, d(0, 2) = 3 d(0,0)=0,d(0,1)=2,d(0,2)=3;
- d ( 1 , 0 ) = 2 , d ( 1 , 1 ) = 0 , d ( 1 , 2 ) = 1 d(1, 0) = 2, d(1, 1) = 0, d(1, 2) = 1 d(1,0)=2,d(1,1)=0,d(1,2)=1;
- d ( 2 , 0 ) = 3 , d ( 2 , 1 ) = 1 , d ( 2 , 2 ) = 0 d(2, 0) = 3, d(2, 1) = 1, d(2, 2) = 0 d(2,0)=3,d(2,1)=1,d(2,2)=0。
初始时的 P P P 指标为 ( 2 + 3 + 1 ) × 2 = 12 (2 + 3 + 1) \times 2 = 12 (2+3+1)×2=12,不满足 P ≤ Q = 10 P \leq Q = 10 P≤Q=10;
第一天, 0 0 0 号城市进行道路改善,改善后的图示如下:
注意到边 ( 0 , 2 ) (0, 2) (0,2) 的值减小了 1 1 1,但 ( 0 , 1 ) (0, 1) (0,1) 并没有减小,因为 L 0 , 1 = 2 L_{0,1} = 2 L0,1=2 ,所以 ( 0 , 1 ) (0, 1) (0,1) 的值不可以再减小了。此时每对顶点之间的灰尘度最小的路线对应的灰尘度为:
- d ( 0 , 0 ) = 0 , d ( 0 , 1 ) = 2 , d ( 0 , 2 ) = 3 d(0, 0) = 0, d(0, 1) = 2, d(0, 2) = 3 d(0,0)=0,d(0,1)=2,d(0,2)=3,
- d ( 1 , 0 ) = 2 , d ( 1 , 1 ) = 0 , d ( 1 , 2 ) = 1 d(1, 0) = 2, d(1, 1) = 0, d(1, 2) = 1 d(1,0)=2,d(1,1)=0,d(1,2)=1,
- d ( 2 , 0 ) = 3 , d ( 2 , 1 ) = 1 , d ( 2 , 2 ) = 0 d(2, 0) = 3, d(2, 1) = 1, d(2, 2) = 0 d(2,0)=3,d(2,1)=1,d(2,2)=0。
此时 P P P 仍为 12 12 12。
第二天,1 号城市进行道路改善,改善后的图示如下:
此时每对顶点之间的灰尘度最小的路线对应的灰尘度为:
- d ( 0 , 0 ) = 0 , d ( 0 , 1 ) = 2 , d ( 0 , 2 ) = 2 d(0, 0) = 0, d(0, 1) = 2, d(0, 2) = 2 d(0,0)=0,d(0,1)=2,d(0,2)=2,
- d ( 1 , 0 ) = 2 , d ( 1 , 1 ) = 0 , d ( 1 , 2 ) = 0 d(1, 0) = 2, d(1, 1) = 0, d(1, 2) = 0 d(1,0)=2,d(1,1)=0,d(1,2)=0,
- d ( 2 , 0 ) = 2 , d ( 2 , 1 ) = 0 , d ( 2 , 2 ) = 0 d(2, 0) = 2, d(2, 1) = 0, d(2, 2) = 0 d(2,0)=2,d(2,1)=0,d(2,2)=0。
此时的 P P P 指标为 ( 2 + 2 ) × 2 = 8 < Q (2 + 2) \times 2 = 8 < Q (2+2)×2=8<Q,此时已经满足条件。
所以答案是 2 2 2。
【评测用例规模与约定】
- 对于 30 % 30\% 30% 的评测用例, 1 ≤ n ≤ 10 1 \leq n \leq 10 1≤n≤10, 0 ≤ L i , j ≤ D i , j ≤ 10 0 \leq L_{i,j} \leq D_{i,j} \leq 10 0≤Li,j≤Di,j≤10;
- 对于 60 % 60\% 60% 的评测用例, 1 ≤ n ≤ 50 1 \leq n \leq 50 1≤n≤50, 0 ≤ L i , j ≤ D i , j ≤ 1 0 5 0 \leq L_{i,j} \leq D_{i,j} \leq 10^5 0≤Li,j≤Di,j≤105;
- 对于所有评测用例, 1 ≤ n ≤ 100 1 \leq n \leq 100 1≤n≤100, 0 ≤ L i , j ≤ D i , j ≤ 1 0 5 0 \leq L_{i,j} \leq D_{i,j} \leq 10^5 0≤Li,j≤Di,j≤105, 0 ≤ Q ≤ 2 31 − 1 0 \leq Q \leq 2^{31} - 1 0≤Q≤231−1。
蓝桥杯 2022 国赛 A 组 F 题。
思路:因为对于P的定义已经给出很明显的提示了——多源最短路径,用Floyd,然后思考一下发现天数越多越容易满足条件,要找最少的天数,那就可以来考虑二分
代码
#include<iostream>
using namespace std;
typedef long long ll;
const int maxn = 100;
const int maxl = 1e5;
const int maxd = 1e5;
const ll maxq = 0xFFFFFFFF - 1;
const int N = 0x3f3f3f3f;
int map[maxn][maxn];
int lim[maxn][maxn];
int ans = -1;
int n;
ll q;
int dist[maxn][maxn];
bool floyd(int day)
{
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
dist[i][j] = map[i][j];
}
}
for (int i = 0; i < n; i++)
{
for(int j = 0; j < n; j++)
dist[i][j] = dist[j][i] = max(dist[i][j] - day / n - (day % n >= i + 1 ? 1 : 0), lim[i][j]);
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
for (int z = 0; z < n; z++)
{
dist[j][z] = min(dist[j][z], dist[j][i] + dist[i][z]);
}
}
}
ll sum = 0;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
sum += dist[i][j];
}
}
if(sum >= 0 && sum <= q)
{
ans = day;
return true;
}
else
return false;
}
void bsearch()
{
int l = -1, r = maxn * maxl + 1;
while (l + 1 != r)
{
int m = (r + l) / 2;
if (floyd(m))
r = m;
else
l = m;
}
}
int main()
{
cin >> n >> q;
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cin >> map[i][j];
}
}
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
cin >> lim[i][j];
}
}
bsearch();
cout << ans;
return 0;
}