单源最短路: 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();
}
}