【模板】 最短路
单源最短路
\(Dijkstra\)
堆优化,用距离最小的点更新其他节点。
- 不能处理有负权边的图。
\(O(mlogn)\)
void dijkstra(int s) {
priority_queue <pair<int,int>,vector<pair<int,int> >,greater<pair<int,int> > > q;
dis[s] = 0;
q.push(make_pair(0,s));
while(!q.empty()) {
int u = q.top().second;
q.pop();
if(vis[u])continue;
vis[u] = true;
for(int i = head[u]; i; i = next[i]) {
int v = to[i];
if(dis[v] <= dis[u] + val[i]) continue;
dis[v] = dis[u] + val[i];
q.push(make_pair(dis[v],v));
}
}
}
\(SPFA\)
\(Bellman-ford\)的队列优化。
每次把能更新且不在队列里的点压入队列。
- 可以用来判负环(一个点被压入队列超过\(n\)次);
- 可以用来求差分约束
将式子\(x_i-x_j \le k\)变形为\(x_i\le x_j + k\),连边\(x_j \rightarrow x_i\),权值为\(k\),求单源最短路
不稳定,最坏情况\(O(mn)\)
void spfa(int s) {
queue <int> q;
dis[s] = 0;
q.push(s);
vis[s] = true;
while(!q.empty()) {
int u = q.front();
q.pop();
vis[u] = false;
for(int i = head[u]; i; i = nxt[i]) {
int v = to[i];
if(vis[v])continue;
if(dis[v] <= dis[u] + val[i])continue;
dis[v] = dis[u] + val[i];
q.push(v);
vis[v] = true;
}
}
}
多源最短路
\(Floyd\)
先枚举中转点。
有时候可以用于每次加入一个点的情况。
\(O(n^3)\)
void Floyd() {
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]);
}
k短路
\(A*\)
先建反向边求出\(t\)到每个点的最短路,即为估计值。
\(bfs\),优先队列的节点按当前距离+估计值排序。每个点都记录路径。
每次走到\(t\)计数\(+1\)。
小数据范围骗分用
struct node {
int id,now,sum;
vector <int> path;
bool operator < (const node & N) const {
return sum > N.sum;
}
};
void bfs(int s,int t) {
node u = (node) {
s,0,dis[s]
};
u.path.push_back(s);
priority_queue <node> q;
q.push(u);
while(!q.empty()) {
u = q.top();
q.pop();
if(u.id == t && ++tot == k) {
for(int i = 0; i < u.path.size(); i++)
printf("%d ",u.path[i]);
return;
}
for(int i = head[u.id]; i; i = nxt[i]) {
bool visit = false;
for(int j = 0; j < u.path.size(); j++)
if(to[i] == u.path[j]) {
visit = true;
break;
}
if(visit) continue;
node v = (node) {
to[i],u.now+w[i],u.now+w[i]+dis[to[i]],u.path
};
v.path.push_back(to[i]);
q.push(v);
}
}
printf("No");
}
最短路分层图
有\(k\)次免费或打折机会,\(k\)很小
拆点\(x,x+n...x+k*n\)
用掉一次机会即\(x+i*n\rightarrow y+(i+1)*n\)
这样建边后正常跑最短路即可。
for(int i = 1; i <= k; i++) {
add(x+i*n,y+i*n,w);
add(x+(i-1)*n,y+i*n,0);
}