目录
2. 单源最短路径 Dijkstra算法(非负)(链式向前星+优先队列 优化)
3. SPFA算法(可负)(Shortest Path Fast Algorithm)
4. EX: POJ-3662-Telephone Lines
6. EX BZOJ2200 [Usaco2011 Jan] 道路和航线
8. EX POJ1094 Sorting It All Out
9. EX POJ1734 Sightseeing trip
1. 链式向前星(数组模拟链表实现邻接表)
链式向前星的空间复杂度:O(n+m)
const int N=100010;//点
const int M=1000010;//边
int head[N],ver[M],edge[M],Next[M],d[N];//头节点,边的尾点,权值,指向的上一条边,点N到起点的距离
int n,m,tot;
//加入有向边(x,y),权值为z
void add(int x,int y,int z){
ver[++tot]=y,edge[tot]=z; // 真实数据
Next[tot]=head[x],head[x]=tot; //从表头x处插入
}
//初始化
void init(){
tot=0;
memset(head,0,sizeof head);
}
//访问从x出发的所有边
for(int i=head[x];i;i=Next[i]){
int y=ver[i],z=edge[i];
// 找到了一条有向边(x,y),权值为z
}
2. 单源最短路径 Dijkstra算法(非负)(链式向前星+优先队列 优化)
//dijkstra单源最短路径:链式向前星+优先队列 O((m+n)logn)
#include<bits/stdc++.h>
using namespace std;
const int N=100010;
const int M=1000010;
const int INF=0x3f3f3f3f;
int head[N],ver[M],edge[M],Next[M],d[N];//头节点,边的尾点,权值,指向的上一条边,点N到起点的距离
bool v[N];
int n,m,tot;
priority_queue<pair<int,int> > pq;//大根堆,pair第二维为节点编号,第一维为dist的相反数(利用相反数变成小根堆)
void add(int x,int y,int z){
ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
}
void init(){
tot=0;
memset(head,0,sizeof head);
}
void dijkstra(){
int s=1; // 起点
memset(d,0x3f,sizeof d); //dist数组
memset(v,0,sizeof v); //节点标记
d[s]=0;
pq.push(make_pair(-d[s],s)); //起点入队
while(pq.size()){
int x=pq.top().second; pq.pop(); //取出堆顶
if(v[x]) continue; //已经找到最短路
v[x]=1;
//扫描所有边
for(int i=head[x];i;i=Next[i]){
int y=ver[i],z=edge[i];
if(d[y]>d[x]+z){
//更新,把新的二元组插入堆
d[y]=d[x]+z;
pq.push(make_pair(-d[y],y));
}
}
}
}
int main(){
while(cin>>n>>m&&n&&m){
init();
for(int i=1;i<=m;i++){
int x,y,z;
cin>>x>>y>>z;
add(x,y,z);
add(y,x,z);
}
dijkstra();
cout<<d[n]<<endl;
}
return 0;
}
3. SPFA算法(可负)(Shortest Path Fast Algorithm)
#include<bits/stdc++.h>
using namespace std;
const int INF=INT_MAX/10;
const int N=100010;
const int M=1000010;
int head[N],ver[M],edge[M],Next[M],d[N];//头节点,边的尾点,权值,指向的上一条边,点N到起点的距离
int Neg[M]; //判断负圈
int pre[N]; //记录前驱节点
bool inq[N]; //inq[i]=true表示节点i在队列中
int n,m,tot;
void print_path(int s,int t){ //打印从s到t的最短路径
if(s==t){cout<<s; return;} //打印起点
print_path(s,pre[t]); //先打印前一个点
cout<<t; //后打印当前点,最后打印的是终点t
}
void init(){
tot=0;
memset(head,0,sizeof head);
}
void add(int x,int y,int z){
ver[++tot]=y,edge[tot]=z,Next[tot]=head[x],head[x]=tot;
}
int spfa(int s){//s是起点
memset(Neg,0,sizeof Neg);
Neg[s]=1;
for(int i=1;i<=n;i++){d[i]=INF,inq[i]=false;} //初始化
d[s]=0; //起点到自己距离为0
queue<int> q;
q.push(s);
inq[s]=true; //起点进入队列
while(q.size()){
int u=q.front(); q.pop(); //队头出队
inq[u]=false;
for(int i=head[u];i;i=Next[i]){
int v=ver[i],w=edge[i];
if(d[v]>d[u]+w){ //u的第i个邻居v,它借道u,到s更近
d[v]=d[u]+w; //更新第i个邻居到s的距离
pre[v]=u;
if(!inq[v]){//邻居v更新状态了,但是它不在队列中,把它放进队列
inq[v]=true;
q.push(v);
Neg[v]++;
if(Neg[v]>n) return 1; //出现负圈
}
}
}
}
cout<<d[n]<<endl; //从s到n的最短距离
//print_path(s,n);
return 0;
}
int main(){
while(cin>>n>>m&&n&&m){
init();
while(m--){
int u,v,w;
cin>>u>>v>>w;
add(u,v,w);
add(v,u,w);
}
spfa(1);
}
return 0;
}
4. EX: POJ-3662-Telephone Lines
做法一:分层图最短路
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
int n, p, k, vis[1010][1010], d[1010][1010];
typedef pair<int,int> P;
struct Nod {
int v, c, w;
bool operator < (const Nod &a) const {
return w > a.w;
}
};
vector<P> G[1010];
void spfa(int s) {
memset(d,-1,sizeof(d));memset(vis,0,sizeof(vis));
d[s][0] = 0;
priority_queue<Nod> que;
que.push((Nod){s,0,0});
while(!que.empty()) {
int u = que.top().v, c = que.top().c; que.pop();
if(vis[u][c])continue;
vis[u][c] = 1;
for(int i = 0; i < G[u].size(); i ++) {
int v = G[u][i].first, w = G[u][i].second;
if(!vis[v][c] && (d[v][c] == -1 || d[v][c] > max(d[u][c],w))) {
d[v][c] = max(d[u][c], w);
que.push((Nod){v,c,d[v][c]});
}
if(c < k) {
if(!vis[v][c+1] && (d[v][c+1] == -1 || d[v][c+1] > d[u][c])) {
d[v][c+1] = d[u][c];
que.push((Nod){v,c+1,d[v][c+1]});
}
}
}
}
}
int main() {
ios::sync_with_stdio(false);
cin>>n>>p>>k;
int s = 1, t = n;
for(int i = 1; i <= p; i ++) {
int u, v, w;
cin>>u>>v>>w;
G[u].push_back(P(v,w));
G[v].push_back(P(u,w));
}
spfa(s);
int ans = 10000010;
for(int i = 0; i <= k; i ++) ans = min(ans,d[t][i]);
printf("%d\n",ans);
return 0;
}
做法二:二分答案+双端队列BFS
对于边权只有0和1的最短路问题,可以使用双端队列BFS求解,复杂度。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <deque>
#include <vector>
using namespace std;
#define maxv 1005
struct edge
{
int v, w;
edge(int a=0,int b=0){v=a,w=b;}
};
int V, E, K, d[maxv];
bool used[maxv];
vector<edge> G[maxv];
int bfs(int m)
{
memset(d, 0x3f, sizeof(d));
memset(used, 0, sizeof(used));
deque<int> q;
d[1] = 0;
q.push_back(1);
while (!q.empty())
{
int u = q.front();
q.pop_front();
if (used[u])
continue;
used[u] = 1;
for (int i = 0; i < (int)G[u].size(); ++i)
{
edge &e = G[u][i];
if (e.w > m)
{
if (d[e.v] > d[u] + 1)
d[e.v] = d[u] + 1, q.push_back(e.v);
}
else
{
if (d[e.v] > d[u])
d[e.v] = d[u], q.push_front(e.v);
}
}
}
return d[V];
}
int main()
{
scanf("%d%d%d", &V, &E, &K);
int maxl = 0;
for (int i = 0; i < E; ++i)
{
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
maxl = max(maxl, w);
G[v].push_back(edge(u, w));
G[u].push_back(edge(v, w));
}
if (bfs(maxl))
{
puts("-1");
return 0;
}
int lb = -1, ub = maxl;
while (ub - lb > 1)
{
int mid = (lb + ub) >> 1;
if (bfs(mid) <= K)
ub = mid;
else
lb = mid;
}
printf("%d\n", ub);
return 0;
}
做法三:二分答案+dijkstra
对于二分的判定,dijkstra求最短路,复杂度。
5. EX CH6101 / P1073 最优贸易
#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 6, M = 1e6 + 6;
int n, m, tot, Price[N], Ans;
int Head[N], Side[M], Next[M], ans[N];
int fHead[N], fSide[M], fNext[M], fans[N];
priority_queue<pair<int, int> > q;
priority_queue<pair<int, int> > fq;
int main() {
cin >> n >> m;
for (int i = 1; i <= n; i++) scanf("%d", &Price[i]);
for (int i = 1; i <= m; i++) {
int x, y, z;
scanf("%d %d %d", &x, &y, &z);
Side[++tot] = y;
Next[tot] = Head[x];
Head[x] = tot;
fSide[tot] = x;
fNext[tot] = fHead[y];
fHead[y] = tot;
if (z == 2) {
Side[++tot] = x;
Next[tot] = Head[y];
Head[y] = tot;
fSide[tot] = y;
fNext[tot] = fHead[x];
fHead[x] = tot;
}
}
memset(ans, 0x3f, sizeof(ans));
memset(fans, 0xcf, sizeof(fans));
ans[1] = Price[1];
fans[n] = Price[n];
q.push(make_pair(-ans[1], 1));
fq.push(make_pair(fans[n], n));
while (q.size()) {
int x = q.top().second;
q.pop();
for (int i = Head[x]; i; i = Next[i]) {
int y = Side[i];
if (ans[y] > ans[x]) {
ans[y] = ans[x];
ans[y] = min(ans[y], Price[y]);
q.push(make_pair(-ans[y], y));
}
}
}
while (fq.size()) {
int x = fq.top().second;
fq.pop();
for (int i = fHead[x]; i; i = fNext[i]) {
int y = fSide[i];
if (fans[y] < fans[x]) {
fans[y] = fans[x];
fans[y] = max(fans[y], Price[y]);
fq.push(make_pair(fans[y], y));
}
}
}
for (int i = 1; i <= n; i++) Ans = max(Ans, fans[i]-ans[i]);
cout << Ans << endl;
return 0;
}
6. EX BZOJ2200 [Usaco2011 Jan] 道路和航线
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
#include<vector>
using namespace std;
typedef long long ll;
const int N=3e5+10,M=3e5+10;
const int INF=0x7f7f7f7f;
inline int read()
{
int x=0,f=1; char ch=getchar();
while(ch<'0' || ch>'9'){if(ch=='-')f=-1; ch=getchar();}
while(ch>='0' && ch<='9'){x=x*10+ch-'0'; ch=getchar();}
return x*f;
}
struct edge
{
int x,y,c,next;
}a[N]; int len,last[N];
void ins(int x,int y,int c)
{
a[++len].x=x;a[len].y=y;a[len].c=c;
a[len].next=last[x];last[x]=len;
}
bool v[N];
int cnt;
vector<int> has[N];
int bl[N];
void dfs(int x)
{
v[x]=1;
has[cnt].push_back(x);
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(!v[y])bl[y]=bl[x],dfs(y);
}
}
struct node
{
int d,id;
bool operator <(const node &b) const
{
return d>b.d;
}
};
priority_queue<pair<int,int> > q;
int deg[N];
int n,R,P,st;
int list[M],head,tail;
int dis[N];
int main()
{
// freopen("a.in","r",stdin);
// freopen("a.out","w",stdout);
n=read(); R=read(); P=read(); st=read();
len=0; memset(last,0,sizeof(last));
for(int i=1;i<=R;i++)
{
int x=read(),y=read(),c=read();
ins(x,y,c);
ins(y,x,c);
}
//get scc
cnt=0; memset(bl,0,sizeof(bl));
memset(v,0,sizeof(v));
for(int i=1;i<=n;i++)
if(!v[i])
{
cnt++; bl[i]=cnt;
dfs(i);
}
//rebuild to DAG
memset(deg,0,sizeof(deg));
for(int i=1;i<=P;i++)
{
int x=read(),y=read(),c=read();
ins(x,y,c); ins(bl[x]+n,bl[y]+n,c);
}
//Get deg
head=1,tail=1;
list[1]=bl[st]+n;
memset(v,0,sizeof(v));
while(head<=tail)
{
int x=list[head];
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(!v[y])v[y]=1,list[++tail]=y;
deg[y-n]++;
}
head++;
}
//Dij+Topsort
memset(dis,0x7f,sizeof(dis)); dis[st]=0;
memset(v,0,sizeof(v));
head=1,tail=1;
list[1]=bl[st];
while(head<=tail)
{
int cc=list[head];
// for(int i=1;i<=n;i++)if(bl[i]==cc)q.push(make_pair(-dis[i],i));
int siz=has[cc].size();
for(int i=0;i<siz;i++) q.push(make_pair(-dis[has[cc][i]],has[cc][i]));
while(q.size())
{
int x=q.top().second;q.pop();
if(v[x])continue;
v[x]=1;
for(int k=last[x];k;k=a[k].next)
{
int y=a[k].y;
if(bl[x]!=bl[y]){deg[bl[y]]--;
if(deg[bl[y]]==0)list[++tail]=bl[y];}
if(dis[y]>dis[x]+a[k].c)
{
dis[y]=dis[x]+a[k].c;
if(bl[x]==bl[y])q.push(make_pair(-dis[y],y));
}
}
}
head++;
}
for(int i=1;i<=n;i++)
{
if(dis[i]==INF) puts("NO PATH");
else printf("%d\n",dis[i]);
}
return 0;
}
7. 任意两点间最短路径
Floyd算法
#include<bits/stdc++.h>
using namespace std;
const int MAXN=105;
const int INF=0x3f3f3f3f;
int graph[MAXN][MAXN];//邻接矩阵存图
int n,m;//n个点,m个边
void floyd(){
int s=1; //定义起点
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
if(graph[i][k]!=INF)
for(int j=1;j<=n;j++)
if(graph[i][j]>graph[i][k]+graph[k][j])
graph[i][j]=graph[i][k]+graph[k][j];
cout<<graph[s][n]<<endl;
}
int main(){
while(~scanf("%d%d",&n,&m)&&n&&m){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
graph[i][j]=INF;//任意两点间初始距离为无穷大
while(m--){
int a,b,c;
cin>>a>>b>>c;
graph[a][b]=graph[b][a]=c;//邻接矩阵存图
}
floyd();
}
return 0;
}
传递闭包
//传递闭包 floyd算法
#include<bits/stdc++.h>
using namespace std;
const int MAXN=105;
const int INF=0x3f3f3f3f;
int graph[MAXN][MAXN];//邻接矩阵存图
int n,m;//n个点,m个边
void floyd(){
int s=1; //定义起点
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
if(graph[i][k]!=INF)
for(int j=1;j<=n;j++)
graph[i][j] |= graph[i][k] & graph[k][j];
cout<<graph[s][n]<<endl;
}
int main(){
while(~scanf("%d%d",&n,&m)&&n&&m){
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
graph[i][j]=INF;//任意两点间初始距离为无穷大
while(m--){
int a,b,c;
cin>>a>>b>>c;
graph[a][b]=graph[b][a]=c;//邻接矩阵存图
}
floyd();
}
return 0;
}