复健

基本上参考 alex-wei 老师的博客

图论

Dijkstra

简单地说,一个点的最短路由长度非严格更小的最短路更新得到,dis 最小的结点一定不会被其它结点更新。

对于这句话的话,我们只要考虑每次拿出 dis 最小的点,去扩展即可,因为 s->p->q 这最短路上,走过的 s->p 的路径一定是最短路,否则替换为最短路之后更短。

因此考虑如何每次拿出 dis 最小的点?我们考虑维护一个距离的非严格递增的数据结构,每次拿出距离最小的点 u,去扩展得到 v,也就是比之前到 v 的路径更短的时候,成功扩展。但问题是此时的 v 不一定是最短路,没关系,我们照样把它压进去,因为等到下一次拿出来 v 的时候,一定是其最短路拿出来的。

为什么?

  1. v 被 u 所扩展,说明存在 \(dis_v>dis_u+e_i\),如此显然 v 不可能在 u 之前就扩展完成,否则如果 v 先出现,那 \(dis_v<dis_u\)

  2. v 被拿出来了,会不会有更短的?显然不会,因为比 \(dis_v\) 短的已经扩展完了,而后面的点扩展到 \(v\) 一定是失败的,因为距离更长了嘛。

可是我们没有数据结构动态维护 \(dis\) 这个东西,因此我们需要记录每次扩展得到的路径的长度,并在取出时进行判断是否为其最短路,很显然,,第一次取出 x 的时候一定是它的最短路。

本质上是利用已有的确定最短路的节点去不断拓展

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int N=(int)(2e5+5);
const int inf=(int)(2e18);
struct node {
	int x,dis;
	node(int X,int Dis) {
		x=X; dis=Dis;
	}
	bool operator < (const node &rhs) const {
		return dis>rhs.dis;
	}
};
//bool operator < (const node &a,const node &b) {
//	return a.dis>b.dis;
//}
// 写法2,把他扔在外面。
struct edge {
	int nex,to,w;
}e[N<<1];
int n,m,S,cnt,dis[N],hea[N];

void add_edge(int x,int y,int z) {
	e[++cnt].nex=hea[x]; e[cnt].to=y; e[cnt].w=z; hea[x]=cnt;
}

priority_queue<node>q;
bool vis[N];
void dij() {
	for(int i=1;i<=n;i++) dis[i]=inf;
	dis[S]=0; q.push(node(S,0));
	while(!q.empty()) {
		int x=q.top().x; q.pop();
		if(vis[x]) continue ;
		vis[x]=1;
		for(int i=hea[x];i;i=e[i].nex) {
			int y=e[i].to;
			if(dis[y]>dis[x]+e[i].w) {
				dis[y]=dis[x]+e[i].w;
				q.push(node(y,dis[y]));
			}
		}
	}
}

signed main() {
	cin.tie(0); ios::sync_with_stdio(false);
	cin>>n>>m>>S;
	for(int i=1;i<=m;i++)  {
		int x,y,z; cin>>x>>y>>z;
		add_edge(x,y,z);
	}
	dij();
	for(int i=1;i<=n;i++) cout<<dis[i]<<' ';
	return 0;
}

SPFA

考虑不断扩展,一条最短路顶多 \(n\) 个点,\(n-1\) 条边,如果出现负环的话,就不止了,因为我一直走负环肯定是更短的,因此有负环的时候会有的点的最短路超过 \(n-1\) 条边。

考虑不断拿出点扩展即可,注意如果点已经在队列中了,那就只更新,不压入,其实只是减少常数罢了。

只要存在某个时刻的最短路径超过 \(n-1\) 条边即可,因为任何时刻,任何一条正权的最短路边数一定最多为 \(n-1\)

#include <bits/stdc++.h>
#define int long long
#define pb push_back
using namespace std;
const int N=(int)(2e5+5);
const int inf=(int)(2e18);
struct edge {
	int nex,to,w;
}e[N<<1];
int n,m,S,cnt,dis[N],hea[N];

void add_edge(int x,int y,int z) {
	e[++cnt].nex=hea[x]; e[cnt].to=y; e[cnt].w=z; hea[x]=cnt;
}

bool vis[N];
queue<int>q;
int bian[N];
bool spfa() {
	while(!q.empty()) q.pop();
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++) bian[i]=0,dis[i]=inf;
	q.push(1); dis[1]=0; vis[1]=1;
	while(!q.empty()) {
		int x=q.front(); q.pop();
		vis[x]=0;
		for(int i=hea[x];i;i=e[i].nex) {
			int y=e[i].to;
			if(dis[y]>dis[x]+e[i].w) {
				dis[y]=dis[x]+e[i].w;
				bian[y]=bian[x]+1;
				if(bian[y]>=n) {
					return 1;
				}
				if(!vis[y]) vis[y]=1,q.push(y);
			}
		}
	}
	return 0;
} 

void sol() {
	cin>>n>>m; cnt=0;
	memset(hea,0,sizeof(hea));
	for(int i=1;i<=m;i++) {
		int x,y,z;
		cin>>x>>y>>z;
		if(z>=0) add_edge(x,y,z),add_edge(y,x,z);
		else add_edge(x,y,z);
	}
	if(spfa()) cout<<"YES\n";
	else cout<<"NO\n";
}

signed main() {
	cin.tie(0); ios::sync_with_stdio(false);
	int T; cin>>T; while(T--) sol();
	return 0;
}
posted @ 2025-06-19 17:04  FxorG  阅读(7)  评论(0)    收藏  举报