最短路/次短路

单源最短路: dijkstra和spfa。dijkstra可以用于求解没有负的最短路时间复杂度为elogv(v是点数,e是边数) spfa可以求解没有负的最短路时间复杂度为Ke(K是常数,e是边数),最坏时间复杂度为ve

多源最短路:floyd 可以求解没有负的最短路 时间复杂度是n^3

 dijkstra O(ElogV)

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=1e3+5;
const int inf=1e9+7;
int n;
struct node{
	int to,val;
	node(int x,int y):to(x),val(y){}
};
vector<node>vec[maxn];
int d[maxn];
int dijkstra(){
	memset(d,inf,sizeof(d));
	priority_queue<pair<int,int>,vector<pair<int,int>  > >que;
	d[1]=0;
	que.push({1,0});
	while(que.size()){
		pair<int,int>now=que.top();
		que.pop();
		int from=now.first,tmpval=now.second;
		if(d[from]<tmpval){
			continue;
		}
		for(int i=0;i<vec[from].size();i++){
			int to=vec[from][i].to,val=vec[from][i].val;
			if(d[to]>d[from]+val){
				d[to]=d[from]+val;
				que.push({to,d[to]});
			}
		}
	}
	return d[n];
}
signed main(){
	int m;
	cin>>n>>m;
	while(m--){
		int x,y,val;
		cin>>x>>y>>val;
		vec[x].push_back(node(y,val));
		vec[y].push_back(node(x,val));
	}
	int ans=dijkstra();
	cout<<ans;
}

dijkstra最短路计数

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxn=2e6+5;
const int inf=1e9+7;
const int mod=100003;
vector<pair<int,int>>vec[maxn];
int dist[maxn],cnt[maxn];
void solve(){
	memset(dist,inf,sizeof(dist));
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		int from,to;
		cin>>from>>to;
		vec[from].push_back({1,to});
		vec[to].push_back({1,from});
	}
	priority_queue<pair<int,int>,vector<pair<int,int>>,greater<pair<int,int>>>q;
	dist[1]=0;
	cnt[1]=1;
	q.push({0,1});
	while(q.size()){
		pair<int,int>now=q.top();
		q.pop();
		int vall=now.first,from=now.second;
		if(dist[from]<vall){
			continue;
		}
		for(auto v:vec[from]){
			int to=v.second,val=v.first;
			if(dist[to]==dist[from]+val){
				cnt[to]=(cnt[to]+cnt[from])%mod;
			}
			else if(dist[to]>dist[from]+val){
				cnt[to]=cnt[from];
				dist[to]=dist[from]+val;
				q.push({dist[to],to});
			}
		}
	}
	for(int i=1;i<=n;i++){
		cout<<cnt[i]<<"\n";
	}
}
signed main(){
	int t;
	t=1;
	while(t--){
		solve();
	}
}

01bfs解决最短路问题

在边权相当于只有0和1的时候可以用01bfs解决最短路问题,利用deque把边权为0的点插入队首,把边权为1的点插入队尾,每次都是取队首,这样可以保证用最小值更新答案

https://atcoder.jp/contests/abc246/tasks/abc246_e

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=1e9+7;
int n,m,ax,ay,bx,by;
int dx[4] = {1, -1, 1, -1}, dy[4] = {1, -1, -1, 1};
const int maxn=1600;
int a[maxn][maxn];
int dist[maxn][maxn][4];
int vis[maxn][maxn][4];
struct node{
	int x,y,opt;
	node(int a,int b,int c):x(a),y(b),opt(c){}
};
void bfs(){
	deque<node>q;
	memset(dist,0x3f,sizeof(dist));
	for(int i=0;i<4;i++){
		dist[ax][ay][i]=0;
	}
	for(int i=0;i<4;i++){
		int nowx=ax+dx[i],nowy=ay+dy[i];
		if(nowx>=1&&nowx<=n&&nowy>=1&&nowy<=m&&a[nowx][nowy]==1){
			dist[nowx][nowy][i]=1;
			q.push_back(node(nowx,nowy,i));
		}
	}
	while(q.size()){
		auto now=q.front();
		//cout<<now.x<<" "<<now.y<<" "<<now.opt<<"\n";
		q.pop_front();
		if(now.x==bx&&now.y==by){
			cout<<dist[bx][by][now.opt];
			return;
		}
		if(vis[now.x][now.y][now.opt]){
			continue;
		}
		vis[now.x][now.y][now.opt]=1;
		for(int i=0;i<4;i++){
			int nowx=now.x+dx[i],nowy=now.y+dy[i];
			if(nowx>=1&&nowx<=n&&nowy>=1&&nowy<=m&&a[nowx][nowy]==1&&vis[nowx][nowy][i]==0){
				int cost=(i==now.opt)?0:1;
				if(dist[nowx][nowy][i]>dist[now.x][now.y][now.opt]+cost){
					if(cost==1){
						dist[nowx][nowy][i]=dist[now.x][now.y][now.opt]+cost;
						q.push_back(node(nowx,nowy,i));
					}
					else{
						dist[nowx][nowy][i]=dist[now.x][now.y][now.opt]+cost;
						q.push_front(node(nowx,nowy,i));
					}
				}
			}
		}
	}
	cout<<"-1";
}
signed main(){
	cin>>n>>ax>>ay>>bx>>by;
	m=n;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			char tmp;
			cin>>tmp;
			if(tmp=='.'){
				a[i][j]=1;
			}
			else{
				a[i][j]=0;
			}
		}
	}
	bfs();
}

spfa  平均时间复杂度O(KE),最坏时间复杂度O(VE)


#include <bits/stdc++.h>
using namespace std;
#define MAXN 10005
#define INF 0x7fffffff
int n,m,s,dis[MAXN];
vector<pair<int,int> > g[MAXN];//用vector存图,但是据说链式前向星更快
void spfa(){
    queue<int> q;
    q.push(s);//把初始点加入队列
    fill(dis+1,dis+1+n,INF);//因为一开始所有点都到不了,所以初始化为INF
    dis[s]=0;//自己到自己肯定距离为0
    while(!q.empty()){
        int u=q.front();
        q.pop();//从队列里取出第一个元素
        for(int i=0;i<g[u].size();i++){
            int v=g[u][i].first,w=g[u][i].second;
            if(dis[v]>dis[u]+w){
                dis[v]=dis[u]+w;
                q.push(v);
            }
            //如果能松弛成功,那么松弛,把松弛成功的目标点放入队列
        }
    }
}
int main(){
    scanf("%d%d%d",&n,&m,&s);
    for(int i=1;i<=m;i++){
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        g[u].push_back(make_pair(v,w));
    }//输入,建图
    spfa();
    for(int i=1;i<=n;i++){
        printf("%d ",dis[i]);
    }//输出
    return 0;
}

spfa求不等式

形如x<=y+5,这样求出来的x是上确界的最小值(即x的最大值),把等式变形为y>=x-5,这样求出来的y是y的下确界的最大值(即y的最小值),所以如果要求最大值就要把等式化为<=,然后y->x连上5这条边后求最短路,要求最小值就要把等式化为>=,然后x->y连上-5这条边后求最长路。并且spfa求最大值即最短路的顺利运行条件是无负环,spfa求最小值即最长路的顺利运行条件是无正环,否则说明等式矛盾!!!,即没有满足等式条件的情况

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+5;
const int inf=1e9+7;
struct node{
	int to,val;
	node(int x,int y):to(x),val(y){}
};
vector<node>vec[maxn];
int vis[maxn];
int dis[maxn];
int cnt[maxn];
int n,m;
bool spfa(){ 
	memset(dis,-0x3f,sizeof(dis));
	dis[0]=0;
	queue<int>q;
	q.push(0);
	vis[0]=1;
	while(!q.empty()){
		int now=q.front();
		q.pop();
		vis[now]=0;
		
		for(auto it:vec[now]){
			int to=it.to,val=it.val;
			//cout<<"from "<<now;
			//cout<<"to "<<to<<" val "<<val<<"\n";
			if(dis[now]+val>dis[to]){
				//cout<<"from "<<now<<" dis[from] "<<dis[now]<<" val "<<val<<" to "<<to<<" dis[to] "<<
				//dis[to]<<"\n";
				cnt[to]=cnt[now]+1;
				if(cnt[now]>=n+1){
					return false;
				}
				dis[to]=dis[now]+val;
				if(!vis[to]){
					vis[to]=1;
					q.push(to);
				}
			}
		}
	}
	return true;
}
void solve(){
	cin>>n>>m;
	while(m--){
		int x,a,b;
		cin>>x>>a>>b;
		if(x==1){
			vec[a].push_back(node(b,0));
			vec[b].push_back(node(a,0));
		}
		else if(x==2){
			vec[a].push_back(node(b,1));
		}
		else if(x==3){
			vec[b].push_back(node(a,0));
		}
		else if(x==4){
			vec[b].push_back(node(a,1));
		}
		else{
			vec[a].push_back(node(b,0));
		}
	}
	for(int i=1;i<=n;i++){
		vec[0].push_back(node(i,1));
	}
	bool flag=spfa();
	if(!flag){
		cout<<"-1"<<"\n";
		return;
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		ans+=dis[i];
	}
	cout<<ans<<"\n";
}
signed main(){
	ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
	int t=1;
	//cin>>t;
	while(t--){
		solve();
	}
}

spfa解决01分数规划

 注意此题当中得把一条语句当作一条边,单词两边的2个word当作点,不然边的条数会很多t掉,我这里代码用的是负环,实现跟上面有点不一样但是思路是一样的

#include<bits/stdc++.h>
#define int long long
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const double epx=1e-7;
const int maxn=1005;
const int inf=1e9+7;
struct node{
	int to,val;
	node(int x,int y):to(x),val(y){}
};
vector<node>vec[maxn];
int n;
int vis[maxn];
int cnt[maxn];
double d[maxn];
bool check(double x){
	memset(vis,0,sizeof vis);
	memset(cnt,0,sizeof cnt);
	queue<int>q;
	for(int i=0;i<=700;i++){
		q.push(i);
		vis[i]=1;
	}
	while(!q.empty()){
		int now=q.front();
		q.pop();
		vis[now]=0;
		for(auto it:vec[now]){
			int to=it.to,val=it.val;
			//cout<<"to "<<to<<"\n";
			if(d[to]>d[now]+x-val+epx){
				//cout<<val<<"\n";
				d[to]=d[now]+x-val;
				cnt[to]=cnt[now]+1;
				if(cnt[to]>=n+1){
					return true;
				}
				if(!vis[to]){
					vis[to]=1;
					q.push(to);
				}
			}
		}
	}
	return false;
}
void solve(){
	while(1){
		cin>>n;
		if(n==0){
			break;
		}
		else{
			for(int i=0;i<=700;i++){
				vec[i].clear();
			}
		}
		for(int i=1;i<=n;i++){
			string s;
			cin>>s;
			int len=s.size();
			if(len>=2){
				int left=(s[0] - 'a') * 26 + s[1] - 'a';
				int right = (s[len - 2] - 'a') * 26 + s[len - 1] - 'a';
				vec[left].push_back(node(right,len));
			}
		}
		double l=0,r=1e3;
		double ans;
		while(r-l>epx){
			double m=(l+r)/2.0;
			if(check(m)){
				ans=m;
				l=m;
			}
			else{
				r=m;
			}
		}
		cout<<fixed<<setprecision(2)<<ans<<"\n";
	}
}
signed main(){
	int t=1;
	while(t--){
		solve();
	}
}

floyd  O(n^3)

typedef struct
{
    char vertex[VertexNum];                                //顶点表
    int edges[VertexNum][VertexNum];                       //邻接矩阵,可看做边表
    int n,e;                                               //图中当前的顶点数和边数
}MGraph;
void Floyd(MGraph g)
{
   int A[MAXV][MAXV];
   int path[MAXV][MAXV];
   int i,j,k,n=g.n;
   for(i=1;i<=n;i++)
      for(j=1;j<=n;j++)
      {
             A[i][j]=g.edges[i][j];
            path[i][j]=-1;
       }
   for(k=1;k<=n;k++)
   {
        for(i=1;i<=n;i++)
           for(j=1;j<=n;j++)
               if(A[i][j]>(A[i][k]+A[k][j]))
               {
                     A[i][j]=A[i][k]+A[k][j];
                     path[i][j]=k;
                }
     }
}

此外对于floyd的节点更新,如果某些边是后来加上去的可以这样更新 链接

floyd的三层循环里面的最外层循环的k代表以k节点来作为中转更新最短路,所以当添加的k节点之后就直接用这个节点来更新最短路

#include<iostream>
#include<cstdio>
#define N 205
using namespace std;
int n,m;
int a[N];
int f[N][N];//邻接矩阵存边
inline void updata(int k){
	for(int i=0;i<n;i++)
	for(int j=0;j<n;j++)
	if(f[i][j]>f[i][k]+f[j][k])
	f[i][j]=f[j][i]=f[i][k]+f[j][k];//用这个新的更新所有前面的 
	return;
}
int main(){
	cin>>n>>m;
	for(int i=0;i<n;i++)
	scanf("%d",a+i);//依次输入每一个村庄建立完成时需要的时间
	for(int i=0;i<n;i++)
	for(int j=0;j<n;j++){
		f[i][j]=1e9;//初始化为保证它不爆炸范围内的最大值 
	}
	for(int i=0;i<n;i++)
	f[i][i]=0;
	int s1,s2,s3;
	for(int i=1;i<=m;i++){
		scanf("%d%d%d",&s1,&s2,&s3);
		f[s1][s2]=f[s2][s1]=s3;//初始化边长 
	}
	int q;
	cin>>q;
	int now=0;
	for(int i=1;i<=q;i++){//处理各询问 
		scanf("%d%d%d",&s1,&s2,&s3);
		while(a[now]<=s3&&now<n){
			updata(now);//依次更新点,使它可以被用来更新其他的点 
			now++;
		}
		if(a[s1]>s3||a[s2]>s3)cout<<-1<<endl;
		else {
			if(f[s1][s2]==1e9)cout<<-1<<endl;
			else cout<<f[s1][s2]<<endl;
		}
	}
	return 0;
} 

求有边数限制的最短路

链接

用bellmanford方法,此外为了避免串联要再定义一个b数组来存储上次更新的结果,每次都用上次的b数组来更新这次的d数组来确保没有更新了d[1]后又用d[1]更新d[2]这样一次循环更新两条边的情况

#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+5;
int n;
int m;
int k;
struct edge{
	int from,to,val;
};
edge a[maxn];
int b[maxn],d[maxn];
void bellmanford(){
	memset(d,0x3f,sizeof(d));
	d[1]=0;
	for(int i=1;i<=k;i++){
		memcpy(b,d,sizeof(d));
		for(int j=1;j<=m;j++){			
			int from=a[j].from,to=a[j].to,val=a[j].val;
			d[to]=min(d[to],b[from]+val);
		}
	}
	if(d[n]>0x3f3f3f3f/2){
		cout<<"impossible"<<"\n";
	}
	else{
		cout<<d[n]<<"\n";
	}
}
int main(){
	cin>>n>>m>>k;
	int cnt=0;
	int w=m;
	while(w--){
		int from,to,val;
		cin>>from>>to>>val;
		a[++cnt]={from,to,val};
	}
	bellmanford();
}

dijkstra求次短路

1.dist[i][0]表示到点i的最短路,dist[i][i]表示到点i的次短路

最短路何时更新:当dist[ i ][ 0 ] > len(j) + val( j , i )时,直接更新最短路

何时更新次短路:   dist[ i ][ 0 ]  < len(j) + val( j , i )  < dist[ i ][ 1 ]时,更新次短路

此外不止更新最短路时要把节点入队,当更新次短路时也要把节点入队。因为设当前求出了1-y的次短路,有可能1-x的的次短路就==1-x的次短路+x到y的最短路

#include <iostream>
#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef pair<int,int>P;
const int maxn = 100000 + 7;
struct Edge{
	int to,next,val;
}edge[maxn];
int n,m,head[maxn],dist[maxn][2],tot;
void addEdge(int a,int b,int c){
	edge[tot].to = b;edge[tot].val = c;edge[tot].next = head[a];head[a] = tot++;
}
void dijkstra(int s){
	for(int i = 0;i<=n;i++)dist[i][0] = dist[i][1] = INF;
	dist[s][0] = 0;
	priority_queue<P,vector<P>, greater<P> >que;
	que.push(P(0,s));
	while(!que.empty()){
		P p = que.top();
		que.pop();
		if(p.first > dist[p.second][1])continue;
		for(int i = head[p.second];~i;i = edge[i].next){
			int d = p.first + edge[i].val;
			if(dist[edge[i].to][0] > d){//更新最短路
				swap(dist[edge[i].to][0] , d);//交换!!!
				que.push(P(dist[edge[i].to][0],edge[i].to));//注意d值已经被交换了
			}
			if(dist[edge[i].to][1] > d&&dist[edge[i].to][0] < d){//更新次短路
				dist[edge[i].to][1] = d;
				que.push(P(d,edge[i].to));
			}
		}
	}
}

最短路和次短路计数 链接

#include<bits/stdc++.h>
#define int long long
#define io ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
const int maxn=2e5+5;
const int inf=1e9+7;
int n,m;
struct node{
	int to,val;
};
struct nodee{
	int from,val,type;
	nodee(int x,int y,int z):from(x),val(y),type(z){}
};
bool operator<(nodee a,nodee b){
	return a.val>b.val;
}
vector<node>vec[maxn];
int dis[maxn][2];
int vis[maxn][2];
int cnt[maxn][2];
void dijkstra(int s,int f){
	memset(dis,inf,sizeof(dis));
	memset(vis,0,sizeof(vis));
	memset(cnt,0,sizeof(cnt));
	dis[s][0]=0;
	cnt[s][0]=1;
	priority_queue<nodee,vector<nodee>,less<nodee>>q;
	q.push(nodee(s,0,0));	
	while(!q.empty()){
		nodee now=q.top();
		q.pop();
		int from=now.from,val=now.val,type=now.type;
		if(vis[from][type]) continue;
		vis[from][type]=1;
		for(node it:vec[from]){
			int to=it.to,vall=it.val;
			if(dis[to][0]>val+vall){
				dis[to][1]=dis[to][0];
				cnt[to][1]=cnt[to][0];
				q.push(nodee(to,dis[to][1],1));
				dis[to][0]=val+vall;
				cnt[to][0]=cnt[from][type];
				q.push(nodee(to,dis[to][0],0));
			}
			else if(dis[to][0]==val+vall){
				cnt[to][0]+=cnt[from][type];
			}
			else if(dis[to][1]>val+vall){
				dis[to][1]=val+vall;
				cnt[to][1]=cnt[from][type];
				q.push(nodee(to,dis[to][1],1));
			}
			else if(dis[to][1]==val+vall){
				cnt[to][1]+=cnt[from][type];
			}
		}
	}
	int ans=cnt[f][0];
	if(dis[f][1]==dis[f][0]+1){
		ans+=cnt[f][1];
	}
	cout<<ans<<"\n";
}
void solve(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		vec[i].clear();
	}
	while(m--){
		int u,v,w;
		cin>>u>>v>>w;
		vec[u].push_back({v,w});
	}
	int s,f;
	cin>>s>>f;
	dijkstra(s,f);
}
signed main(){
	int t=1;
	cin>>t;
	while(t--){
		solve();
	}
}

k短路

链接

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值