复健
基本上参考 alex-wei 老师的博客
图论
Dijkstra
简单地说,一个点的最短路由长度非严格更小的最短路更新得到,dis 最小的结点一定不会被其它结点更新。
对于这句话的话,我们只要考虑每次拿出 dis 最小的点,去扩展即可,因为 s->p->q 这最短路上,走过的 s->p 的路径一定是最短路,否则替换为最短路之后更短。
因此考虑如何每次拿出 dis 最小的点?我们考虑维护一个距离的非严格递增的数据结构,每次拿出距离最小的点 u,去扩展得到 v,也就是比之前到 v 的路径更短的时候,成功扩展。但问题是此时的 v 不一定是最短路,没关系,我们照样把它压进去,因为等到下一次拿出来 v 的时候,一定是其最短路拿出来的。
为什么?
-
v 被 u 所扩展,说明存在 \(dis_v>dis_u+e_i\),如此显然 v 不可能在 u 之前就扩展完成,否则如果 v 先出现,那 \(dis_v<dis_u\)
-
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;
}