一.dijikstra
题目描述
如题,给出一个有向图,请输出从某一点出发到所有点的最短路径长度。
输入格式
第一行包含三个整数 n,m,s,分别表示点的个数、有向边的个数、出发点的编号。
接下来 m 行每行包含三个整数 u,v,w表示一条 u→v的,长度为 w 的边。
输出格式
输出一行 n 个整数,第 i 个表示 s 到第i 个点的最短路径,若不能到达则输出2^31−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
说明/提示
【数据范围】
对于 20%20\%20% 的数据:1≤n≤51\le n \le 51≤n≤5,1≤m≤151\le m \le 151≤m≤15;
对于 40%40\%40% 的数据:1≤n≤1001\le n \le 1001≤n≤100,1≤m≤1041\le m \le 10^41≤m≤104;
对于 70%70\%70% 的数据:1≤n≤10001\le n \le 10001≤n≤1000,1≤m≤1051\le m \le 10^51≤m≤105;
对于 100%100\%100% 的数据:1≤n≤1041 \le n \le 10^41≤n≤104,1≤m≤5×1051\le m \le 5\times 10^51≤m≤5×105,保证数据随机。
#include<bits/stdc++.h>
using namespace std;
const int inf=INT_MAX;
struct node{//使用链式前向星储存边,模拟链表
int u,v,w,nxt;
}edge[1000010];
int head[100010],tot;
void add(int u,int v,int w){
tot++;
edge[tot].nxt=head[u];
edge[tot].u=u;
edge[tot].v=v;
edge[tot].w=w;
head[u]=tot;
}
int dis[100010],vis[100010];//dis存放起点到每个点的距离,vis记录点是否到达
struct Node{//构造结构体,重载小于号,方便后面构造优先队列
int w,now;//w为dis的大小,now为边的权重
bool operator <(const Node &x)const{
return w>x.w;
}
};
void dijkstra(int n,int s){
for(int i=1;i<=n;i++) dis[i]=inf;
dis[s]=0;
priority_queue<Node> q;//构造优先队列,使得dis[]最小的数放在队列最前面,减少时间复杂度
q.push((Node){0,s});
while(!q.empty()){
Node k=q.top();
int x=k.now;
q.pop();
if(vis[x]==1) continue;
vis[x]=1;
for(int i=head[x];i;i=edge[i].nxt){
if(dis[edge[i].v]>dis[edge[i].u]+edge[i].w){
dis[edge[i].v]=dis[edge[i].u]+edge[i].w;
q.push((Node){dis[edge[i].v],edge[i].v});
}
}
}
}
int main(){
int n,m,s;
int u,v,w;
cin>>n>>m>>s;
for(int i=0;i<m;i++){
cin>>u>>v>>w;
add(u,v,w);
}
dijkstra(n,s);
for(int i=1;i<=n;i++){
if(dis[i]>=inf) cout<<2147483647<<' ';
else cout<<dis[i]<<' ';
}
}
二.弗洛伊德算法
问题描述
小H有n个秘密基地(编号 1 到 n ),n个秘密基地之间有 m 条双向路径和 w 个单向时空隧道,通过路径需要消耗一定的时间Ti,而通过时空隧道可以使时光倒流Tj,现在小H想知道他能否从某一秘密基地出发,通过路径和时空隧道回到过去(即回到出发的秘密基地且该时刻要早于出发时间)。
输入格式
第1行,一个整数 F,表示测试用例的数量
接下来对于每一个测试用例,输入格式如下
第1行,三个空格分隔的整数n,m,w
第222到 m+1 行,三个空格分隔的数字 s,e,t,表示 s,e 之间存在双向道路,通行需要消耗t,允许两点间存在多条路径
第m+2到m+w+1行三个空间分隔的数字 s,e,t,表示存在从 s 到 e 的单向时空隧道,穿过时空隧道时光倒流 t
输出格式
对于每个测试用例,如果小H能回到过去则输出YES
,否则输出NO
每个测试用例的输出占一行
样例输入
2
3 3 1
1 2 2
1 3 4
2 3 1
3 1 3
3 2 1
1 2 3
2 3 4
3 1 8
样例输出
NO
YES
数据规模和约定
1≤n≤500,1≤m≤4×10^4,1≤w≤200
1≤Ti,Tj≤10^4
思路:
直接把正边和负边一起代入计算,最后如果从x出发回到x的距离dis[x][x]<0,则代表能够时间倒流
#include<bits/stdc++.h>
using namespace std;
int dis[510][510];
const int INF=999999;
int main(){
int k,n,m,w,flag;
cin>>k;
while(k--){
flag=0;
cin>>n>>m>>w;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(i==j) dis[i][j]=0;
else dis[i][j]=INF;
}
int x,y,z;
for(int i=0;i<m;i++){
cin>>x>>y>>z;
dis[x][y]=dis[y][x]=z;
}
for(int i=0;i<w;i++){
cin>>x>>y>>z;
dis[x][y]=-z;
}
for(int k=1;k<=n;k++)
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]);
for(int i=1;i<=n;i++){
if(dis[i][i]<0) flag=1;
}
if(flag==1) cout<<"YES"<<endl;
else cout<<"NO"<<endl;
}
}