图论·最短路径
最短路径
一、Dijkstra 单源最短路径
Dijkstra是在非负权图上求单源最短路径的方法,复杂度
当一个点
贪心地想,对于被影响的点
但是,dij不能跑带负环的也不能跑最长路。
板子代码:
void dijkstra()
{
memset(dis,0x3f,sizeof dis);
dis[s]=0;
q.push({0,s});
while (!q.empty())
{
int u=q.top().second;
q.pop();
if (vis[u]) continue;
vis[u]=true;
int _size=e[u].size();
for (int i=0;i<_size;i++)
{
int v=e[u][i].nxt,w=e[u][i].val;
if (dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
q.push({dis[v],v});
}
}
}
}
二、SPFA 单源最短路径——用队列优化的Bellman-Ford
与
SPFA的优势是边权可以为负,也可以判负环
板子代码↓
bool spfa()
{
memset(dis,0x3f,sizeof dis);
memset(neq,0,sizeof neq);
memset(inq,0,sizeof inq);
queue <int> q;
q.push(1),neq[1]++,dis[1]=0,inq[1]=true;
while (!q.empty())
{
int u=q.front(),q.pop();
inq[u]=false;
int _size=e[u].size();
for (int i=0;i<_size;i++)
{
int v=e[u][i].nxt,w=e[u][i].val;
if (dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
if (inq[v]) continue;
q.push(v),inq[v]=true;
neq[v]++;
if (neq[v]>n) return true;//有负环
}
}
}
return false;
}
还有一个最优比率环,不会待补
三、Floyd 多源最短路
十分暴力的最短路方法,代码简单但效率不高,在某些场景下有自己的优势。
用滚动数组将dp方程优化到二维
判断负环:若存在
传递闭包:
板子代码:
for (int k=1;k<=n;k++)//一定要在外层循环k,因为它是逐步递推的
{
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++)
f[i][j]=min(f[i][j],f[i][k]+f[k][j]);
}
}
四、差分约束系统
这个东西是一种特殊的
将约束条件
【YbtOj】例题
A.单源最短路径
贴
#incIude <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;
const int N=1e5+5;
int n,m,s;
struct node{
int nxt,val;
};
vector <node> e[N];
priority_queue < pii,vector <pii>,greater<pii> > q;
int dis[N];
void dijkstra()
{
memset(dis,0x3f,sizeof dis);
dis[s]=0;
q.push({0,s});
while (!q.empty())
{
int u=q.top().second;
q.pop();
int _size=e[u].size();
for (int i=0;i<_size;i++)
{
int v=e[u][i].nxt,w=e[u][i].val;
if (dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
q.push({dis[v],v});
}
}
}
}
signed main()
{
scanf("%lld%lld%lld",&n,&m,&s);
for (int i=1;i<=m;i++)
{
int u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
e[u].push_back({v,w});
}
dijkstra();
for (int i=1;i<=n;i++) printf("%lld ",dis[i]);
return 0;
}
B.负环判断
贴
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=2e3+5;
int T;
int n,m;
struct node{
int nxt,val;
};
vector <node> e[N];
int inq[N];
int dis[N];
int cnt[N];
queue <int> q;
bool spfa()
{
memset(inq,0,sizeof inq);
memset(dis,0x3f,sizeof dis);
memset(cnt,0,sizeof cnt);
while (!q.empty()) q.pop();
q.push(1);
inq[1]=1;
dis[1]=0;
cnt[1]++;
while (!q.empty())
{
int u=q.front();
q.pop();
inq[u]=0;
int _size=e[u].size();
for (int i=0;i<_size;i++)
{
int v=e[u][i].nxt,w=e[u][i].val;
if (dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
if (inq[v]==0)
{
inq[v]=1;
q.push(v);
cnt[v]++;
if (cnt[v]>=n) return true;
}
}
}
}
return false;
}
signed main()
{
scanf("%lld",&T);
while (T--)
{
scanf("%lld%lld",&n,&m);
for (int i=1;i<=n;i++) e[i].clear();
for (int i=1;i<=m;i++)
{
int u,v,w;
scanf("%lld%lld%lld",&u,&v,&w);
if (w<0) e[u].push_back({v,w});
else
{
e[u].push_back({v,w});
e[v].push_back({u,w});
}
}
if (spfa()) printf("YE5\n");
else printf("N0\n");
}
return 0;
}
C.最优贸易
显然,我们需要找到一条路径,在前面某段找到点权最小值,在后面某段找到点权最大值,这两个值的差值就是答案。再看这句话,可以转化为求源点到某点
以上方法有误,
(错误代码)
#include <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;
const int N=1e5+5;
int n,m;
int c[N];
vector <int> e1[N],e2[N];
priority_queue < pii > q1;
priority_queue < pii,vector <pii>,greater<pii> > q2;
bool vis1[N],vis2[N];
int dis1[N],dis2[N];
int ans;
void dijkstra1()//最大的
{
memset(dis1,0xc1,sizeof dis1);
dis1[n]=max(dis1[n],c[n]);
q1.push({dis1[n],n});
while (!q1.empty())
{
int u=q1.top().second;
q1.pop();
if (vis1[u]) continue;
vis1[u]=true;
int _size=e2[u].size();
for (int i=0;i<_size;i++)
{
int v=e2[u][i];
if (max(dis1[u],c[v])>dis1[v])
{
dis1[v]=max(dis1[u],c[v]);
q1.push({dis1[v],v});
}
}
}
}
void dijkstra2()//最大的
{
memset(dis2,0x3f,sizeof dis2);
dis2[1]=min(dis2[1],c[1]);
q2.push({dis2[1],1});
while (!q2.empty())
{
int u=q2.top().second;
q2.pop();
if (vis2[u]) continue;
vis2[u]=true;
int _size=e1[u].size();
for (int i=0;i<_size;i++)
{
int v=e1[u][i];
if (min(dis2[u],c[v])<dis2[v])
{
dis2[v]=min(dis2[u],c[v]);
q2.push({dis2[v],v});
}
}
}
}
signed main()
{
scanf("%lld%lld",&n,&m);
for (int i=1;i<=n;i++) scanf("%lld",&c[i]);
for (int i=1;i<=m;i++)
{
int x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
if (z==1) e1[x].push_back(y),e2[y].push_back(x);
else
{
e1[x].push_back(y),e1[y].push_back(x);
e2[x].push_back(y),e2[y].push_back(x);
}
}
dijkstra1();
dijkstra2();
for (int i=1;i<=n;i++) ans=max(ans,dis1[i]-dis2[i]);
printf("%lld",ans);
return 0;
}
D.汽车加油
注意到
贴
#incIude <bits/stdc++.h>
#define int long long
#define pin pair<int,node>
using namespace std;
const int N=105;
const int K=12;
int n,k,a,b,c;
int mp[N][N];
struct node{
int u,v,k,w;
bool operator < (const node &x) const{
return x.w<w;
}
};
priority_queue <node> q;
int vis[N][N][K];
int f[N][N][K];//f[i][j][k]:走到i,j还剩k步的最小费用
int dx[4]={0,1,0,-1},dy[4]={1,0,-1,0};
int ans=9e18;
void dijkstra()
{
memset(f,0x3f,sizeof f);
f[1][1][k]=0;
q.push((node){1,1,k,0});
while (!q.empty())
{
int x=q.top().u,y=q.top().v,cost=q.top().k;
q.pop();
if (vis[x][y][cost]) continue;
vis[x][y][cost]=true;
if (mp[x][y]&&cost!=k)
{
if (f[x][y][k]>f[x][y][cost]+a)
{
f[x][y][k]=f[x][y][cost]+a;
q.push((node){x,y,k,f[x][y][k]});
}
continue;
}else{
if (f[x][y][k]>f[x][y][cost]+a+c){
f[x][y][k]=f[x][y][cost]+a+c;
q.push((node){x,y,k,f[x][y][k]});
}
}
if (cost>0)
{
for (int i=0;i<4;i++)
{
int xx=x+dx[i],yy=y+dy[i];
if (xx<1||xx>n||yy<1||yy>n) continue;
int w;
if (i<=1) w=0;
else w=b;
if (f[xx][yy][cost-1]>f[x][y][cost]+w)
{
f[xx][yy][cost-1]=f[x][y][cost]+w;
q.push({xx,yy,cost-1,f[xx][yy][cost-1]});
}
}
}
}
}
signed main()
{
scanf("%lld%lld%lld%lld%lld",&n,&k,&a,&b,&c);
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++) scanf("%lld",&mp[i][j]);
}
dijkstra();
for (int i=0;i<=k;i++) ans=min(ans,f[n][n][i]);
printf("%lld",ans);
return 0;
}
E.比较大小
这就是一个
注意到
贴
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+5;
int m,n,q;
int f[N][N];
signed main()
{
scanf("%lld%lld%lld",&m,&n,&q);
int a,b;
while (m--)
{
scanf("%lld%lld",&a,&b);
f[a][b]=1;
}
for (int k=1;k<=n;k++)
{
for (int i=1;i<=n;i++)
{
for (int j=1;j<=n;j++) f[i][j]|=f[i][k]&f[k][j];
}
}
for (int i=1;i<=n;i++)
{
for (int j=1;j<=i;j++)
{
if (f[i][j]&&f[j][i])
{
printf("10000words to copy");
return 0;
}
}
}
while (q--)
{
scanf("%lld%lld",&a,&b);
if (f[a][b]) printf("YES\n");
else if(f[b][a]) printf("NO\n");
else printf("DK\n");
}
return 0;
}
F.删边问题
“最大的权值最小”,一眼二分。每次二分答案删边的价值,边权小于
贴
#incIude <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;
const int N=2e4+5;
int n,m,T;
struct node{
int nxt,len,val;
};
vector <node> e[N];
int mx;
int ans;
int dis[N],vis[N];
int dijkstra(int x)
{
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
priority_queue < pii,vector <pii>,greater <pii> > q;
dis[1]=0;
q.push(make_pair(0,1));
while (!q.empty())
{
int u=q.top().second;
q.pop();
if (vis[u]) continue;
vis[u]=true;
int _size=e[u].size();
for (int i=0;i<_size;i++)
{
int v=e[u][i].nxt,p=e[u][i].val,w=e[u][i].len;
if (p<=x) continue;
if (dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
q.push(make_pair(dis[v],v));
}
}
}
return dis[n];
}
bool check(int x)
{
if (dijkstra(x)>=T) return true;
return false;
}
signed main()
{
scanf("%lld%lld%lld",&n,&m,&T);
for (int i=1,u,v,len,val;i<=m;i++)
{
scanf("%lld%lld%lld%lld",&u,&v,&len,&val);
e[u].push_back((node){v,len,val});
mx=max(mx,val);
}
if (check(0)) { printf("-1 %lld",dis[n]); return 0; }
int l=1,r=mx;
while (l<r)
{
int mid=(l+r)/2;
if (check(mid)) r=mid;
else l=mid+1;
}
printf("%lld",l);
return 0;
}
G.修建道路
注意到所求答案是所选边中剩下边权的最大值
贴
#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e3+5;
const int inf=0x3f3f3f3f3f3f3f3f;
int n,p,k;
struct node{
int v,w;
};
vector <node> e[N];
int dis[N],vis[N];
int dijkstra(int x)
{
priority_queue < pair<int,int> > q;
dis[1]=0;
q.push({0,1});
while (!q.empty())
{
int u=q.top().second;
q.pop();
if (vis[u]) continue;
vis[u]=true;
int _size=e[u].size();
for (int i=0;i<_size;i++)
{
int v=e[u][i].v,w=e[u][i].w;
if (dis[v]>dis[u]+(w>x))
{
dis[v]=dis[u]+(w>x);
q.push({-dis[v],v});
}
}
}
return dis[n];
}
bool check(int x)
{
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
if (dijkstra(x)<=k) return true;
return false;
}
signed main()
{
scanf("%lld%lld%lld",&n,&p,&k);
for (int i=1,u,v,l;i<=p;i++)
{
scanf("%lld%lld%lld",&u,&v,&l);
e[u].push_back({v,l});
e[v].push_back({u,l});
}
int l=0,r=1000001;
while (l<r)
{
int mid=l+r>>1;
if (check(mid)) r=mid;
else l=mid+1;
}
if (r==1000001) printf("-1");
else printf("%lld",r);
return 0;
}
H.最小花费
注意到
贴
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+5;
int n,m,k;
int s,t;
struct node{
int nxt,val;
};
vector <node> e[N];
struct NODE{
int dis,pos,k;
bool operator < (const NODE &x) const{
return x.dis<dis;
}
};
int ans=0x3f3f3f3f3f3f3f3f;
priority_queue <NODE> q;
int dis[N][20],vis[N][20];//到i还能走j个0
void dijkstra()
{
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
dis[s][k]=0;
q.push({0,s,k});
while (!q.empty())
{
int u=q.top().pos,kk=q.top().k;
q.pop();
if (vis[u][kk]) continue;
vis[u][kk]=1;
int _size=e[u].size();
for (int i=0;i<_size;i++)
{
int v=e[u][i].nxt,w=e[u][i].val;
if (dis[u][kk]+w<dis[v][kk])
{
dis[v][kk]=dis[u][kk]+w;
q.push({dis[v][kk],v,kk});
}
if (kk&&dis[u][kk]<dis[v][kk-1])
{
dis[v][kk-1]=dis[u][kk];
q.push({dis[v][kk-1],v,kk-1});
}
}
}
}
signed main()
{
scanf("%lld%lld%lld",&n,&m,&k);
scanf("%lld%lld",&s,&t);
for (int i=1,x,y,v;i<=m;i++)
{
scanf("%lld%lld%lld",&x,&y,&v);
e[x].push_back({y,v});
e[y].push_back({x,v});
}
dijkstra();
for (int i=0;i<=k;i++) ans=min(ans,dis[t][i]);
printf("%lld",ans);
return 0;
}
I.收费站点
注意到答案具有单调性,所以可以二分所交费用的最大值,每次跑
贴
#incIude <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;
const int N=1e4+5;
int inf=1e10;
int n,m,s,t,tol;
int f[N];
struct node{
int nxt,val;
};
vector <node> e[N];
int mx;
int ans=inf;
int dis[N],vis[N];
int dijkstra(int x)
{
priority_queue < pii,vector <pii>,greater <pii> > q;
if (f[s]>x) return inf;
dis[s]=0;
q.push({0,s});
while (!q.empty())
{
int u=q.top().second;
q.pop();
if (vis[u]) continue;
vis[u]=1;
int _size=e[u].size();
for (int i=0;i<_size;i++)
{
int v=e[u][i].nxt,w=e[u][i].val;
if (f[v]>x) continue;
if (dis[v]>dis[u]+w)
{
dis[v]=dis[u]+w;
q.push({dis[v],v});
}
}
}
return dis[t];
}
bool check(int x)
{
memset(dis,0x3f,sizeof dis);
memset(vis,0,sizeof vis);
if (dijkstra(x)<=tol) return true;
else return false;
}
signed main()
{
scanf("%lld%lld%lld%lld%lld",&n,&m,&s,&t,&tol);
for (int i=1;i<=n;i++) scanf("%lld",&f[i]);
for (int i=1,a,b,c;i<=m;i++)
{
scanf("%lld%lld%lld",&a,&b,&c);
e[a].push_back({b,c});
e[b].push_back({a,c});
}
int l=0,r=inf;
while (l<r)
{
int mid=l+r>>1;
if (check(mid)) r=mid,ans=min(ans,r);
else l=mid+1;
}
if (ans==inf) printf("-1");
else printf("%lld",ans);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效