最短路径

首先呢,最短路蒟蒻会的板子只有3个,且都为朴素版qwq
1.floyd(n^3)
就是个暴力转移的算法,注意k,i,j顺序如果不是k,i,j正确性会出问题
板子在这里
代码如下

#include<bits/stdc++.h>
#define int long long
using namespace std;
int dis[210][210];
signed main(){
    int n,m;
    cin>>n>>m;
    memset(dis, 0x3f, sizeof(dis));
    for (int i = 1; i <= m; i ++) {
        int x, y, z;
        cin>>x>>y>>z;
        dis[x][y] = z;
        dis[y][x] = 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++) {
        for (int j=1; j <=n; j++) {
            if (i != j){
                cout<<dis[i][j]<<" ";
            }
            else{
                cout<<0<<" ";
            }
        }
        cout << endl;
    }
    return 0;
}

推荐一道水题
只需要用floyd+O2就可以过qwq
代码如下

#include<bits/stdc++.h>
#define int long long
using namespace std;
int f[1001][1001];
int n,m,u,v,w,ans=0;
signed main(){
    ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
	    for(int j=1;j<=n;j++){
	    	f[i][j]=0x7fffffff;
		}
	}
	for(int i=1;i<=m;i++){
	cin>>u>>v>>w;
	f[u][v]=min(f[u][v],w);
	}
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				if(f[i][j]>f[i][k]+f[k][j]){
					f[i][j]=f[i][k]+f[k][j];
				}
			}
		}
	}
	for(int i=2;i<=n;i++){
		ans+=f[1][i]+f[i][1];
	}
	cout<<ans;
	return 0;
}

2.spfa(出题人没喝那一般就是nm)
板子
算法原理:大概就是不断取出队首的点进行松弛操作(其实就是更新,我也不知道为啥叫这个鬼名),如果可以松弛且不在队列中那就把它加入队列,像这样循环每次取出队首元素更新直到将队列内的元素都弹空
优点可以判负环,跑负边权,但过于容易被卡,只需要一个随机网格图(在网格图中走错一次路可能导致很高的额外开销),或者一个构造过的链套菊花(使得队列更新菊花的次数非常高)即可(from网络世界),这个是真的,所以我们要学习如何优化,但我还不会...,这个等以后会再次更新
代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int inf=0x7fffffff;//2147483647
const int maxm=5e5+5;
const int maxn=1e4+5;
int n,m,s;
int cnt;
int dis[maxn];
int vis[maxn];
int head[maxm];
struct node{
	int nxt,to,dis;
}p[maxm];
void add(int u,int v,int w){
	p[++cnt].nxt=head[u];
	p[cnt].to=v;
	head[u]=cnt;
	p[cnt].dis=w;
}
void spfa(){
	queue<int>q;
	for(int i=1;i<=n;i++){
		dis[i]=inf;
		vis[i]=0;
	}
	q.push(s);
	dis[s]=0;
	vis[s]=1;
	while(!q.empty()){
		int u=q.front();
		q.pop();
		vis[u]=0;
		for(int i=head[u];i;i=p[i].nxt){
			int v=p[i].to;
			if(dis[v]>dis[u]+p[i].dis){
				dis[v]=dis[u]+p[i].dis;
				if(vis[v]==0){
					vis[v]=1;
					q.push(v);
				}
			}
		}
	}
}
	signed main(){
		cin>>n>>m>>s;
		for(int i=1;i<=m;i++){
			int uu,vv,ww;
			cin>>uu>>vv>>ww;
			add(uu,vv,ww);
		}
		spfa();
		for(int i=1;i<=n;i++){
			if(s==i){
				cout<<0<<" ";
			}
			else{
				cout<<dis[i]<<" ";
			}
		}
		return 0;
	}
//diji增加了一个贪心操作,即每回用之前的最短路来更新,而有负边权的话可能需要从不是最短路的那条更新,而spfa则是能入队就入队	

3.dijkstra(mlogn好像是,如果错了请大佬指正)
算法原理:个人觉得就是在spfa基础上加个贪心,用之前的最短路来更新,而有负边权时贪心正确性出错,因为它显然是短视的
板子
代码如下:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,s;
const int mod=(1<<31)-1;
const int maxn=5e5+5; 
int diss[maxn];//源点到当前点的最小路径 
int to[maxn];
int head[maxn];
int nxt[maxn];
int v[maxn];
int cnt;
struct node{
	int id,dis;
	friend bool operator < (node a,node b){
		return a.dis>b.dis;//小根堆 
	} 
};
void add(int x,int y,int z){
	to[++cnt]=y;//儿子 
	nxt[cnt]=head[x];//上一条边
	head[x]=cnt;//边数
	v[cnt]=z; 
}
priority_queue<node>q;
void dj(int s){//起点 
	for(int i=1;i<=n;i++){//赋初值
		diss[i]=mod;
	}
	diss[s]=0;//每回挑出离源点最近的点	
	q.push(node{s,0});
	while(!q.empty()){
	node jt=q.top();//取出堆顶元素
	q.pop();
	int id=jt.id;
	int disss=jt.dis;
	if(disss!=diss[id]){//应该就是这里会被负边权干掉,用vis数组也一样
		continue;//个人理解,如果后面已经被再次更新,就证明可以直接进入下一次 
	}
	for(int i=head[id];i;i=nxt[i]){
		if(diss[to[i]]>diss[id]+v[i]){
			diss[to[i]]=diss[id]+v[i];
			q.push(node{to[i],diss[to[i]]});
		}
	}
	}
}
signed main()
{
	ios::sync_with_stdio(false);
	cin>>n>>m>>s;
	for(int i=1;i<=m;i++){
		int u,v,w;
		cin>>u>>v>>w;
		add(u,v,w);
	}
	dj(s);
	for(int i=1;i<=n;i++){
		cout<<diss[i]<<" ";
	}
 	return 0;
}

例题
汽车行驶问题
简单来说就是建k层图,走的步数不同在不同层,而边权存储走这一步所需价值
代码如下

#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,k,a,b,c;
int s;
int cnt;
const int inf=0x3f3f3f3f;
const int maxn=1e5+5;
int dis[maxn*3];
int vvv;
int vis[maxn*15];
int to[maxn*15];
int nxt[maxn*15];
int v[maxn*15];
int head[maxn*15];
struct node{
	int id,dis;
	friend bool operator < (node a,node b){
		return a.dis>b.dis;
	}
}p[maxn*15];
void add(int x,int y,int z){
	to[++cnt]=y;
	nxt[cnt]=head[x];
	v[cnt]=z;
	head[x]=cnt;
}
void spfa(int s){
	queue<int>q;
	q.push(s);
	dis[s]=0;
	vis[s]=1;
	while(!q.empty()){
		int a=q.front();
		vis[a]=0;
		q.pop();
		for(int i=head[a];i;i=nxt[i]){
			int vv=to[i];
			if(dis[vv]>dis[a]+v[i]){
				dis[vv]=dis[a]+v[i];
				if(!vis[vv]){
					q.push(vv);
					vis[vv]=1;
				}
			}
		}
	}
}
int tr(int i,int j,int k){
	return k*n*n+(i-1)*n+j;
}
bool check (int i,int j){
	return 1<=i&&i<=n&&1<=j&&j<=n;
}
signed main(){
	   ios::sync_with_stdio(false);
	   cin>>n>>k>>a>>b>>c;
	   memset(dis,inf,sizeof(dis));
	   for(int i=1;i<=n;i++){
	   		for(int j=1;j<=n;j++){
	   		int opt;
	   		cin>>opt;
	   		if(opt){
	   			for(int jj=1;jj<=k;jj++){
	   				add(tr(i,j,jj),tr(i,j,0),a);
				  }
				 if(check(i+1,j)){
				 	add(tr(i,j,0),tr(i+1,j,1),0);
				 }
				 if(check(i,j-1)){
				 	add(tr(i,j,0),tr(i,j-1,1),b);
				 }
				 if(check(i-1,j)){
				 	add(tr(i,j,0),tr(i-1,j,1),b);
				 }
				 if(check(i,j+1)){
				 	add(tr(i,j,0),tr(i,j+1,1),0);
				 }
			}
			else{
				for(int jj=1;jj<=k;jj++){
					add(tr(i,j,jj),tr(i,j,0),a+c);
				}
				for(int kk=0;kk<k;kk++){
				if(check(i+1,j)){
				 	add(tr(i,j,kk),tr(i+1,j,kk+1),0);
				 }
				 if(check(i,j-1)){
				 	add(tr(i,j,kk),tr(i,j-1,kk+1),b);
				 }
				 if(check(i-1,j)){
				 	add(tr(i,j,kk),tr(i-1,j,kk+1),b);
				 }
				 if(check(i,j+1)){
				 	add(tr(i,j,kk),tr(i,j+1,kk+1),0);
			}
			}
			}
			}
	   }	
	   spfa(tr(1,1,0));
	   int ans=1e18+2;
	for(int kk=1;kk<=k;kk++){
		ans=min(ans,dis[tr(n,n,kk)]);
	}
	cout<<ans;
	   return 0;
}

the end!!!

posted @ 2023-04-05 00:32  jt0007  阅读(60)  评论(4编辑  收藏  举报