目录
最短路
Floyd
经典三重循环,求多源最短路
#include<iostream>
using namespace std;
int d[1000][1000];
int main()
{
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
if(i==j)
d[i][j]=0;
else
d[i][j]=0x3f3f3f3f;
for(int i=1;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
d[u][v]=w;
d[v][u]=w;
}
for(int k=1;k<=n;k++)
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
d[i][j]=min(d[i][j],d[i][k]+d[k][j]);
}
可以用Floyd传递闭包,判断连通性,总之,看到n的数据范围较小时,便可以向Floyd上靠
Dijkstra
经典求单源最短路
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
struct node
{
int v,w;
};
vector<node> g[1000000];
priority_queue<pair<int,int> > q;
int dis[10000000],vis[10000000];
int main()
{
int n,m;
cin>>n>>m;
memset(dis,0x3f,sizeof(dis));
dis[1]=0;
q.push({0,1});
for(int i=1;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
g[u].push_back({v,w});
}
while(q.size())
{
auto p=q.top();
q.pop();
int u=p.second;
if(vis[u])
continue;
vis[u]=1;
for(auto it:g[u])
{
int v=it.v,w=it.w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
q.push({-dis[v],v});
}
}
}
}
因为使用了优先队列,于是在第39行需要进负数,使得在负数状态下最大,也就是正数状态下最小
应用较广泛,不容易被卡
SPFA
关于SPFA,它死了
SPFA很容易被卡,如菊花图,方格图。
于是,SPFA唯一的作用好像就是判断负环了
#include<iostream>
#include<queue>
#include<cstring>
#include<vector>
using namespace std;
struct node
{
int v,w;
};
vector<node> g[1000000];
queue<int> q;
int cnt[10000000],dis[10000000],vis[10000000];
int main()
{
int n,m;
cin>>n>>m;
memset(dis,0x3f,sizeof(dis));
dis[1]=0;
vis[1]=1;
q.push(1);
for(int i=1;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
g[u].push_back({v,w});
}
while(q.size())
{
int u=q.front();
q.pop();
vis[u]=0;
for(auto it:g[u])
{
int v=it.v,w=it.w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
cnt[v]=cnt[u]+1;
if(cnt[v]>=n)
{
cout<<"负环";
return 0;
}
if(!vis[v])
{
q.push(v);
vis[v]=1;
}
}
}
}
}
在这里,我们引出 差分约束 即形如 的不等式组 由于这些不等式与松弛操作的 if 判断相似,于是我们边可以建立从 到 的有向边,权值为 ,再求最短路,由于不确定 的正负,所以我们便使用SPFA来求,若有负环,则无解。若不连通,则有很多个解
(蒟蒻不会证明)
最短路-分层图
在我们做题的时候,时不时就会碰见那些无赖,比如免费坐飞机。
于是我们便引出分层图
即 有次机会花费权值为一个特殊值 我们便可以建立 张图,每层之间边使用特殊权值,每层之内的边使用正常权值,我们便拿无赖坐飞机举例
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
struct node
{
int v,w;
};
vector<node> g[1000000];
priority_queue<pair<int,int> > q;
int dis[10000000],vis[10000000];
int main()
{
int n,m,k,s,t;
cin>>n>>m>>k>>s>>t;
memset(dis,0x3f,sizeof(dis));
dis[s]=0;
q.push({0,s});
for(int i=1;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
g[u].push_back({v,w});
g[v].push_back({u,w});
for(int j=1;j<=k;j++)
{
g[u+(j-1)*n].push_back({v+j*n,0});
g[v+(j-1)*n].push_back({u+j*n,0});
g[u+j*n].push_back({v+j*n,w});
g[v+j*n].push_back({u+j*n,w});
}
}
while(q.size())
{
auto p=q.top();
q.pop();
int u=p.second;
if(vis[u])
continue;
for(auto it:g[u])
{
int v=it.v,w=it.w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
q.push({-dis[v],v});
}
}
}
int minn=0x3f3f3f3f;
for(int i=0;i<=k;i++)
minn=min(minn,dis[t+i*n]);
cout<<minn;
}
(蒟蒻不知道为什么最后要取最小值)
这个方法占空间比较大,所以还是数组尽量开大一点比较好
最短路计数
顾名思义
若到 点的最短路与松弛后的相等,就证明多了一条路
那就继承
若正常松弛,那证明到 点的路径数量与到 点的相等
那就覆盖
#include<iostream>
#include<vector>
#include<queue>
#include<cstring>
using namespace std;
struct node
{
int v,w;
};
vector<node> g[1000000];
priority_queue<pair<int,int> > q;
int ans[10000000],dis[10000000],vis[10000000];
int main()
{
int n,m;
cin>>n>>m;
memset(dis,0x3f,sizeof(dis));
dis[1]=0;
q.push({0,1});
ans[1]=1;
for(int i=1;i<=m;i++)
{
int u,v;
cin>>u>>v;
g[u].push_back({v,1});
g[v].push_back({u,1});
}
while(q.size())
{
auto p=q.top();
q.pop();
int u=p.second;
if(vis[u])
continue;
vis[u]=1;
for(auto it:g[u])
{
int v=it.v,w=it.w;
if(dis[v]==dis[u]+w)
ans[v]=(ans[u]+ans[v])%100003;
if(dis[v]>dis[u]+w)
{
ans[v]=ans[u];
dis[v]=dis[u]+w;
q.push({-dis[v],v});
}
}
}
for(int i=1;i<=n;i++)
cout<<ans[i]<<endl;
}
次短路
怎么更新次短路?
1.在松弛操作成功后,松弛前的最短路便大于松弛后的最短路,于是次短路便更新为松弛前的最短路
2.松弛操作失败,但松弛后的路径比原来的次短路要小,于是更新次短路
3.次短路更新次短路
#include<iostream>
#include<cstring>
#include<queue>
#include<vector>
using namespace std;
struct node
{
int v,w;
};
vector<node> g[1000000];
queue<int> q;
int dis[10000000],diss[10000000],vis[10000000];
int main()
{
int n,m;
cin>>n>>m;
memset(dis,0x3f,sizeof(dis));
memset(diss,0x3f,sizeof(diss));
dis[1]=0;
q.push(1);
vis[1]=1;
for(int i=1;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
g[u].push_back({v,w});
g[v].push_back({u,w});
}
while(q.size())
{
int u=q.front();
q.pop();
vis[u]=0;
for(auto it:g[u])
{
int v=it.v,w=it.w;
if(dis[v]>dis[u]+w)
{
diss[v]=dis[v];
dis[v]=dis[u]+w;
if(!vis[v])
{
vis[v]=1;
q.push(v);
}
}
if(dis[v]<dis[u]+w&&diss[v]>dis[u]+w)
{
diss[v]=dis[u]+w;
if(!vis[v])
{
q.push(v);
vis[v]=1;
}
}
if(diss[v]>diss[u]+w)
{
diss[v]=diss[u]+w;
if(!vis[v])
{
q.push(v);
vis[v]=1;
}
}
}
}
cout<<diss[n];
}
最长路
顾名思义
我们可以在读边权的时候改成负的,再正常求最短路,最后把负的再负回去
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
struct node
{
int v,w;
};
vector<node> g[100000];
queue<int> q;
int dis[10000000],vis[10000000],cnt[10000000];
int main()
{
int n,m;
cin>>n>>m;
memset(dis,0x3f,sizeof(dis));
dis[1]=0;
q.push(1);
vis[1]=1;
for(int i=1;i<=m;i++)
{
int u,v,w;
cin>>u>>v>>w;
g[u].push_back({v,w});
// g[v].push_back({u,w});
}
while(q.size())
{
int u=q.front();
q.pop();
vis[u]=0;
for(auto it:g[u])
{
int v=it.v,w=-it.w;
if(dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
cnt[v]=cnt[u]+1;
if(cnt[v]>=n)
{
cout<<-1;
return 0;
}
q.push(v);
vis[v]=1;
}
}
}
if(dis[n]==0x3f3f3f3f)
{
cout<<-1;
return 0;
}
cout<<-dis[n];
}