/* 返回顶部 */

【模板】 最短路

单源最短路

\(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);
}
posted @ 2020-07-11 16:47  Mogeko  阅读(109)  评论(0编辑  收藏  举报