最短路问题
Posted mgtnb
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了最短路问题相关的知识,希望对你有一定的参考价值。
算法汇总:
-
Floyd 算法
-
Dijkstra 算法
-
Bellman-Ford 算法
-
SPFA算法
Floyd算法
应该是几个算法当中最简单的了,虽然时间复杂度有点高。
f[k][i][j]表示从i经过若干个编号不超过k的节点到j的最短路长度。于是有:
f[k][i][j]=min(f[k-1][i][j],f[k-1][i][k]+f[k-1][k][j])
k这一维可以被省略,对结果没有影响。
f[i][j]=min(f[i][j],f[i][k]+f[k][j])
传递闭包
通过传递性推导出尽量多的元素之间的关系。
核心代码:
for(int i=1;i<=n;i++) f[i][i]=1; for(int k=1;k<=n;k++) { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { f[i][j]|=f[i][k]&f[k][j]; } } }
Sorting It All Out
描述
An ascending sorted sequence of distinct values is one in which some form of a less-than operator is used to order the elements from smallest to largest. For example, the sorted sequence A, B, C, D implies that A < B, B < C and C < D. in this problem, we will give you a set of relations of the form A < B and ask you to determine whether a sorted order has been specified or not.
输入Input consists of multiple problem instances. Each instance starts with a line containing two positive integers n and m. the first value indicated the number of objects to sort, where 2 <= n <= 26. The objects to be sorted will be the first n characters of the uppercase alphabet. The second value m indicates the number of relations of the form A < B which will be given in this problem instance. Next will be m lines, each containing one such relation consisting of three characters: an uppercase letter, the character "<" and a second uppercase letter. No letter will be outside the range of the first n letters of the alphabet. Values of n = m = 0 indicate end of input.输出For each problem instance, output consists of one line. This line should be one of the following three:
Sorted sequence determined after xxx relations: yyy...y.
Sorted sequence cannot be determined.
Inconsistency found after xxx relations.
where xxx is the number of relations processed at the time either a sorted sequence is determined or an inconsistency is found, whichever comes first, and yyy...y is the sorted, ascending sequence.
样例输入
4 6
A<B
A<C
B<C
C<D
B<D
A<B
3 2
A<B
B<A
26 1
A<Z
0 0
样例输出
Sorted sequence determined after 4 relations: ABCD.
Inconsistency found after 2 relations.
Sorted sequence cannot be determined.
这是一道有向的传递闭包问题,对于每个i<j的式子,令f[i][j]=1,其余都=0。
用Floyd进行传递闭包后,判断如果有f[i][j]=f[j][i]=1,则说明矛盾。输出Inconsistency found after xxx relations.
判断每个点的出度和入度并保存下来,如果加起来不等于n-1,则说明不能确定每一对的大小关系。输出Sorted sequence cannot be determined.
除此之外,可以用拓扑排序,把每个点的顺序保存下来。再输出。
最后附上我没有AC的代码(我真的不知道哪里错了),康康就行了,不要相信它。
#include<bits/stdc++.h> using namespace std; int n,m,tot; bool f[30][30],vis[30]; int in[30],out[30]; char sh[30]; bool floyd() { for(int k=1;k<=n;k++) { for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(f[i][k]&&f[k][j]) f[i][j]=1; } } } for(int i=1;i<n;i++) { for(int j=i+1;j<=n;j++) { if(f[i][j]==1&&f[j][i]==1) return 0; } } return 1; } bool check() { memset(in,0,sizeof(in)); memset(out,0,sizeof(out)); for(int i=1;i<=n;i++) { for(int j=1;j<=n;j++) { if(f[i][j]) in[j]++,out[i]++; } } for(int i=1;i<=n;i++) if(in[i]+out[i]!=n-1) return 0; else return 1; } void topsort() { stack<int> q; int i; memset(vis,0,sizeof(vis)); tot=0; for(i=1;i<=n;i++) { if(in[i]==0) { q.push(i); break; } } while(!q.empty()) { int cur=q.top(); q.pop(); vis[cur]=1; sh[tot++]=cur-1+‘A‘; for(int i=1;i<=n;i++) { if(vis[i]==0&&f[cur][i]) in[i]--; if(vis[i]==0&&in[i]==0&&i!=cur) q.push(i); } } } int read() { int f=1,x=0; char c; c=getchar(); while(c<‘0‘||c>‘9‘) { if(c==‘-‘) f=-1; c=getchar(); } while(c>=‘0‘&&c<=‘9‘) { x=x*10+c-‘0‘; x=x%10000007; c=getchar(); } return x*f; } int main() { while(1) { n=read();m=read(); if(n==0&&m==0) break; memset(f,0,sizeof(f)); int flag1=0,flag2=0; for(int i=1;i<=m;i++) { char a,b,c; cin>>a>>b>>c; f[a-‘A‘+1][c-‘A‘+1]=1; if(flag1||flag2) continue; else if(floyd()==0) { flag1=i; continue; } else if(check()) { topsort(); flag2=i; } } if(flag1) printf("Inconsistency found after %d relations. ",flag1); else if(flag2) printf("Sorted sequence determined after %d relations: %s. ",flag2,sh); else printf("Sorted sequence cannot be determined. "); } return 0; }
Dijkstra 算法
算法流程:
1.初始化dis[1]=0,其余节点的dis值为正无穷大。
2.找出一个没有被标记的、dis[x]最小的节点x,再标记。
3.找节点x的所有出边y,dis[y]=min(dis[y],dis[x]+z)。
4.重复上述步骤,直到所有点都被标记。
注意Dijkstra算法不适用于负权边。
贴上模板 时间复杂度O(N2)
void dijkstra() { memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(cis)); dis[1]=0; for(int i=1;i<n;i++) { int x=0; for(int j=1;j<=n;j++) if(vis[j]==0&&(x==0||dis[j]<dis[x])) x=j; vis[x]=1; for(int y=1;y<=n;y++) dis[y]=min(dis[y],dis[x]+a[x][y]); } }
堆优化(优先队列)
O(mlogn)
priority_queue< pair<int,int> >q; void dijkstra() { memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[1]=0; q.push(make_pair(0,1)); while(q.size()) { int x=q.top().second;q.pop(); if(vis[x]) continue; vis[x]=1; for(int i=head[x];i;i=next[i]) { int y=ver[i],z=edge[i]; if(dis[y]>dis[x]+z) { dis[y]=dis[x]+z; q.push(make_pair(-d[y],y)); } } } }
Bellman-Ford 算法
1.扫描所有边(x,y,z),若dis[y]>dis[x]+z,则用dis[x]+z更新dis[y]。
2.重复上述步骤,直到没有更新操作发生。
时间复杂度为O(nm)。
SPFA 算法
队列优化的Bellman-man算法
1.建立一个队列,初始队列只有起点。
2.取出队头结点x,扫描它的所有出边(x,y,z),若dis[y]>dis[x]+z,则用dis[x]+z更新dis[y]。如果y不在队列中,则把y入队。
3.重复直到队列为空。
时间复杂度 O(km)~O(nm)。
queue<int> q; void spfa() { memset(dis,0x3f,sizeof(dis)); memset(vis,0,sizeof(vis)); dis[1]=0;vis[1]=1; q.push(1); while(q.size()) { int x=q.front();q.pop(); vis[x]=0; for(int i=head[x];i;i=next[i]) { int y=ver[i],z=edge[i]; if(dis[y]>dis[x]+z) { dis[y]=dis[x]+z; if(!vis[y]) q,push(y),vis[y]=1; } } } }
在有负权边的情况下,Bellman-Ford和SPFA算法也能使用。
最优贸易
描述
C国有 n 个大城市和 m 条道路,每条道路连接这 n 个城市中的某两个城市。任意两个城市之间最多只有一条道路直接相连。这 m 条道路中有一部分为单向通行的道路,一部分为双向通行的道路,双向通行的道路在统计条数时也计为1条。
C国幅员辽阔,各地的资源分布情况各不相同,这就导致了同一种商品在不同城市的价格不一定相同。但是,同一种商品在同一个城市的买入价和卖出价始终是相同的。
商人阿龙来到C国旅游。当他得知“同一种商品在不同城市的价格可能会不同”这一信息之后,便决定在旅游的同时,利用商品在不同城市中的差价赚一点旅费。设C国 n 个城市的标号从 1~n,阿龙决定从1号城市出发,并最终在 n 号城市结束自己的旅行。在旅游的过程中,任何城市可以被重复经过多次,但不要求经过所有 n 个城市。
阿龙通过这样的贸易方式赚取旅费:他会选择一个经过的城市买入他最喜欢的商品——水晶球,并在之后经过的另一个城市卖出这个水晶球,用赚取的差价当做旅费。因为阿龙主要是来C国旅游,他决定这个贸易只进行最多一次,当然,在赚不到差价的情况下他就无需进行贸易。
现在给出 n 个城市的水晶球价格,m 条道路的信息(每条道路所连接的两个城市的编号以及该条道路的通行情况)。请你告诉阿龙,他最多能赚取多少旅费。
输入格式
第一行包含 2 个正整数n 和m,中间用一个空格隔开,分别表示城市的数目和道路的
数目。
第二行 n 个正整数,每两个整数之间用一个空格隔开,按标号顺序分别表示这n 个城
市的商品价格。
接下来 m 行,每行有3 个正整数,x,y,z,每两个整数之间用一个空格隔开。如果z=1,表示这条道路是城市x 到城市y 之间的单向道路;如果z=2,表示这条道路为城市x 和城市y 之间的双向道路。
输出格式
一个整数,表示答案。
样例输入
5 5
4 3 5 6 1
1 2 1
1 4 1
2 3 2
3 5 1
4 5 2
样例输出
5
数据范围与约定
输入数据保证 1 号城市可以到达n 号城市。
对于 10%的数据,1≤n≤6。
对于 30%的数据,1≤n≤100。
对于 50%的数据,不存在一条旅游路线,可以从一个城市出发,再回到这个城市。
对于 100%的数据,1≤n≤100000,1≤m≤500000,1≤x,y≤n,1≤z≤2,1≤各城市
水晶球价格≤100。
题目大概意思就是说在带权值的图上找到一条从1到n的路径,使有两个点的权值差最大。
先以1为起点,在原图上用SPFA,保存下一个数组d,表示从1到x的所有路径中,能够经过的权值最小的节点的权值。
在求单源最短路径时,用min(d[x],price[y])更新d[y];
再以n为起点,反图上用SPFA,保存一个f[x],与d类似。
最后枚举每个节点x,用f[x]-d[x]更新答案。
#include<bits/stdc++.h> using namespace std; const int N=100002; vector<int> q1[N],q2[N]; int w[N],a[N],b[N]; queue<int> q; int n,m; inline int read() { int ans=0,f=1; char ch=getchar(); while(!isdigit(ch)) f*=(ch==‘-‘)? -1:1,ch=getchar(); do ans=(ans<<1)+(ans<<3)+(ch^48),ch=getchar(); while(isdigit(ch)); return ans*f; } void spfa1() { q.push(1); memset(a,127,sizeof(a)); while(!q.empty()) { int t=q.front(); q.pop(); a[t]=min(a[t],w[t]); int l=q1[t].size(); for(int i=0;i<l;i++) { if(a[t]<a[q1[t][i]]) { a[q1[t][i]]=a[t]; q.push(q1[t][i]); } } } } void spfa2() { q.push(n); while(!q.empty()) { int t=q.front(); q.pop(); b[t]=max(b[t],w[t]); int l=q2[t].size(); for(int i=0;i<l;i++) { if(b[t]>b[q2[t][i]]) { b[q2[t][i]]=b[t]; q.push(q2[t][i]); } } } } int main() { n=read();m=read(); for(int i=1;i<=n;i++) { w[i]=read(); } for(int i=1;i<=m;i++) { int x,y,z; x=read();y=read();z=read(); q1[x].push_back(y); q2[y].push_back(x); if(z==2) { q1[y].push_back(x); q2[x].push_back(y); } } spfa1(); spfa2(); int ans=0; for(int i=0;i<=n;i++) ans=max(ans,b[i]-a[i]); printf("%d",ans); return 0; }
我昨晚忘记发了QAQ……真的
求不轻罚。
以上是关于最短路问题的主要内容,如果未能解决你的问题,请参考以下文章