文章目录
以下的模版题都是求从1到n的最短路
AcWing 849. Dijkstra求最短路 I
朴素写法
- 邻接矩阵表示,注意重边。
- 重边处理:留下最小的即可
#include<bits/stdc++.h>
using namespace std;
const int maxn=510;
const int INF=0x3F3F3F3F;
int n,m;
int g[maxn][maxn];
int d[maxn];
bool vis[maxn];
void dijkstra(int s){
fill(d,d+maxn,INF);
fill(vis,vis+maxn,false);
d[s]=0;
for(int i=0;i<n;i++){
int u=-1,MIN=INF;
for(int j=1;j<=n;j++){
if(!vis[j]&&d[j]<MIN){
MIN=d[j];
u=j;
}
}
if(u==-1) return;
vis[u]=true;
for(int v=1;v<=n;v++){
if(!vis[v]&&g[u][v]!=INF&&g[u][v]+d[u]<d[v]){
d[v]=g[u][v]+d[u];
}
}
}
}
int main(){
cin>>n>>m;
fill(g[0],g[0]+maxn*maxn,INF);
for(int i=0;i<m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
//注意有可能发生重边
g[a][b]=min(g[a][b],c);
//如果没有重边g[a][b]=c;
}
dijkstra(1);
if(d[n]==INF) printf("-1");
else{
printf("%d",d[n]);
}
}
AcWing 850. Dijkstra求最短路 II
- 邻接表存储。 邻接表没法在读入的时候就删除重边,但过程中会取最小。
- 优先队列优化 出队的如果已经访问过了,直接continue,忽略它即可
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5;
const int INF=0x3F3F3F3F;
typedef pair<int,int> PII;
struct Node{
int v,dis;
};
int n,m;
vector<Node> g[maxn];
int d[maxn];
bool vis[maxn];
void dijkstra(int s){
fill(vis,vis+maxn,false);
fill(d,d+maxn,INF);
d[s]=0;
priority_queue<PII,vector<PII>,greater<PII>> heap;
heap.push({0,s});
while(!heap.empty()){
PII top=heap.top();
heap.pop();
int u=top.second;
int MIN=top.first;
if(vis[u]) continue;
vis[u]=true;
for(int i=0;i<g[u].size();i++){
int v=g[u][i].v;
int dist=g[u][i].dis;
if(!vis[v]&&d[u]+dist<d[v]){
d[v]=d[u]+dist;
heap.push({d[v],v});
}
}
}
}
int main(){
cin>>n>>m;
for(int i=0;i<m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
//注意有可能发生重边 但邻接表存就不用管了
Node node;
node.v=b;
node.dis=c;
g[a].push_back(node);
}
dijkstra(1);
if(d[n]==INF) printf("-1");
else{
printf("%d",d[n]);
}
}
AcWing 853. 有边数限制的最短路
bellman-ford
for n次迭代(n为点数)
for 所有边(a,b,w)
dist[b]=min(dist[b],dist[a]+w);
- 因为每次遍历所有边,所以用结构体存储所有边的起点、终点、权值就可以了
- 因为存在负权边,所以注意判断条件
#include<bits/stdc++.h>
using namespace std;
const int maxn=510;
const int maxm=1e5+10;
const int INF=0x3F3F3F3F;
struct Edge{
int x,y,w;
}edge[maxm];
int n,m,k;
int d[maxn],cp[maxn];
bool BellmanFord(int s){
fill(d,d+maxn,INF);
d[s]=0;
for(int i=0;i<k;i++){
memcpy(cp,d,sizeof d); //防止一次走两步 作一个拷贝
for(int j=0;j<m;j++){
Edge t=edge[j];
d[t.y]=min(d[t.y],cp[t.x]+t.w);
}
}
if(d[n]>0x3f3f3f3f/2) return false; // 注意判断条件
else return true;
}
int main(){
cin>>n>>m>>k;
for(int i=0;i<m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
edge[i]={a,b,c};
}
bool t=BellmanFord(1);
if(!t) printf("impossible");
else printf("%d\n",d[n]);
}
AcWing 851. spfa求最短路
-
对BF算法进行优化,spfa是只有dist[a]变小,dist[b]才会变小。利用宽搜进行优化(队列)
-
用bool数组标记b当前有无入队
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int INF=0x3F3F3F3F;
struct Node{
int v,dis;
};
vector<Node> g[maxn];
int d[maxn];
int n,m;
bool st[maxn];//st是标志现在点i有没有在队列里
bool SPFA(int s){
fill(d,d+maxn,INF);
queue<int> q;
q.push(s);
d[s]=0;
st[s]=true;
while(!q.empty()){
int top=q.front();
q.pop();
st[top]=false;
for(int i=0;i<g[top].size();i++){
int v=g[top][i].v;
int dis=g[top][i].dis;
if(d[v]>d[top]+dis){
d[v]=d[top]+dis;
if(!st[v]){
q.push(v);
st[v]=true;
}
}
}
}
if(d[n]>0x3F3F3F3F/2) return false;
else return true;
}
int main(){
cin>>n>>m;
for(int i=0;i<m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
Node node;
node.v=b;
node.dis=c;
g[a].push_back(node);
}
bool f=SPFA(1);
if(!f) puts("impossible");
else printf("%d",d[n]);
}
AcWing 852. spfa判断负环
统计当前每个点的最短路中所包含的边数,如果某点的最短路所包含的边数大于等于n,则也说明存在环
新加一个数组 cnt[i] 表示走到这个i点所需要的步数,对应dist[i]
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int INF=0x3F3F3F3F;
struct Node{
int v,dis;
};
vector<Node> g[maxn];
int d[maxn],cnt[maxn];
int n,m;
bool st[maxn];//st是标志现在点i有没有在队列里
bool SPFA(){ //返回有无负环
//fill(d,d+maxn,INF);
queue<int> q;
for(int i=1;i<=n;i++){
q.push(i);
st[i]=true;
}
while(!q.empty()){
int top=q.front();
q.pop();
st[top]=false;
for(int i=0;i<g[top].size();i++){
int v=g[top][i].v;
int dis=g[top][i].dis;
if(d[v]>d[top]+dis){
d[v]=d[top]+dis;
cnt[v]=cnt[top]+1; //前一个节点步数+1
if(cnt[v]>=n) return true;//有负环
if(!st[v]){
q.push(v);
st[v]=true;
}
}
}
}
return false; //无负环
}
int main(){
cin>>n>>m;
for(int i=0;i<m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
Node node;
node.v=b;
node.dis=c;
g[a].push_back(node);
}
bool f=SPFA();
if(f) puts("Yes");
else puts("No");
}
AcWing 854. Floyd求最短路
- 注意重边
- 注意初始化图的过程
#include<bits/stdc++.h>
using namespace std;
const int INF=0x3F3F3F3F;
const int maxn=210;
const int maxm=2e5+10;
int n,m,k;
//int g[maxn][maxn]; //不需要g
int d[maxn][maxn];
void floyd(){
for(int k=1;k<=n;k++){
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(d[i][k]!=INF&&d[k][j]!=INF&&d[i][j]>d[i][k]+d[k][j]){
d[i][j]=d[i][k]+d[k][j];
}
}
}
}
}
int main(){
cin>>n>>m>>k;
// fill(g[0],g[0]+maxn*maxn,INF);
fill(d[0],d[0]+maxn*maxn,INF);
for(int i=1;i<=n;i++){
d[i][i]=0;
}
for(int i=0;i<m;i++){
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
//注意重边
d[a][b]=min(d[a][b],c);
d[a][a]=0;d[b][b]=0; //不初始化 那么可能有自环
}
floyd();
while(k--){
int a,b;
scanf("%d%d",&a,&b);
if(d[a][b]>INF/2) printf("impossible\n"); //由于有负权边存在所以约大过INF/2也很合理
else printf("%d\n",d[a][b]);
}
}