Dijkstra堆优化
dijkstra的原理/流程?
dijkstradijkstra本质上的思想是贪心,它只适用于不含负权边的图.
我们把点分成两类,一类是已经确定最短路径的点,称为"白点",另一类是未确定最短路径的点,称为"蓝点"
dijkstradijkstra的流程如下::
1.1. 初始化dis[start] = 0,dis[start]=0,其余节点的disdis值为无穷大.
2.2. 找一个disdis值最小的蓝点x,x,把节点xx变成白点.
3.3. 遍历xx的所有出边(x,y,z),(x,y,z),若dis[y] > dis[x] + z,dis[y]>dis[x]+z,则令dis[y] = dis[x] + zdis[y]=dis[x]+z
4.4. 重复2,32,3两步,直到所有点都成为白点…
时间复杂度为O(n^2)O(n
2
)
dijkstradijkstra为什么是正确的
当所有边长都是非负数的时候,全局最小值不可能再被其他节点更新.所以在第22步中找出的蓝点xx必然满足:dis[x]:dis[x]已经是起点到xx的最短路径…我们不断选择全局最小值进行标记和拓展,最终可以得到起点到每个节点的最短路径的长度
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
#define inf 2147483647
int n,m,s,a,b,c;
struct node
{
int next,to;
int w;
}edge[5000001];
struct fow{
int pt,cost;
};
int top=0;
int dis[5000001],vis[5000001],head[5000001];
struct cmp{
bool operator()(fow a,fow b){
return a.cost>b.cost;//important
}
};
void add(int from,int to,int w)
{
edge[++top].next=head[from];
edge[top].to=to;
edge[top].w=w;
head[from]=top;
}
void dijkstra(){
priority_queue<fow,vector<fow>,cmp> q;
fow r;
r.pt=s;
r.cost=0;
dis[s]=0;
q.push(r);
while(!q.empty()){
fow nt=q.top();
q.pop();
int now=nt.pt;
//int nc=nt.cost;
if(vis[now]) continue;
else vis[now]=1;
for(int i=head[now];i;i=edge[i].next){
int next=edge[i].to;
if(dis[next]>dis[now]+edge[i].w){
dis[next]=dis[now]+edge[i].w;
if(!vis[next]){
fow to_push;
to_push.pt=next;
to_push.cost=dis[next];
q.push(to_push);
}
}
}
}
}
int main()
{
cin>>n>>m>>s;
for(int i=1;i<=n;i++) dis[i]=inf;
for(int i=1;i<=m;i++){
cin>>a>>b>>c;
add(a,b,c);
}
dijkstra();
for(int i=1;i<=n;i++){
cout<<dis[i]<<' ';
}
return 0;
}
最短路计数
SPFA的过程中dp
if(dis[next]==dis[now]+edge[i].w) cnt[next]+=cnt[now];
// luogu-judger-enable-o2
#include<iostream>
#include<queue>
#include<vector>
using namespace std;
#define inf 2147483647
#define maxn 2000001
struct node
{
int u,v;
int w;
}edge[maxn];
int top=0;
int dis[maxn],vis[maxn],head[maxn],cnt[maxn];
void add(int from,int to,int dis)
{
edge[++top].u=head[from];
edge[top].v=to;
edge[top].w=dis;
head[from]=top;
}
int n,m,a,b,c;
void spfa(int s)
{
queue <int> q;
for(int i=1;i<=n;i++)
{
vis[i]=0;
dis[i]=inf;
}
q.push(s);
vis[s]=1;
dis[s]=0;
cnt[s]=1;
while(!q.empty())
{
int now=q.front();
q.pop();
vis[now]=0;
for(int i=head[now];i;i=edge[i].u)
{
int next=edge[i].v;
if(dis[next]>dis[now]+edge[i].w)
{
dis[next]=dis[now]+edge[i].w;
cnt[next]=cnt[now];
if(!vis[next])
{
q.push(next);
vis[next]=1;
}
}
else if(dis[next]==dis[now]+edge[i].w){
cnt[next]+=cnt[now];
cnt[next]%=100003;
}
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
cin>>a>>b;
add(a,b,1);
add(b,a,1);
}
spfa(1);
for(int i=1;i<=n;i++){
cout<<cnt[i]<<endl;
}
return 0;
}
SPFA判负环
一个点最多被访问n-1次吧。。
然后如果访问次数>=n就有负环
复杂度应该不太高
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;
int read(){
int x=0;char ch=getchar();bool pos=1;
for(;!isdigit(ch);ch=getchar()) if(ch=='-') pos=0;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';
return pos?x:-x;
}
int n,m,head[300001],top=0,dis[300001],cnt[300001],vis[300001];
struct node{
int to,next,w;
}edge[300001];
void add(int from,int to,int w){
edge[++top].to=to;
edge[top].w=w;
edge[top].next=head[from];
head[from]=top;
}
int spfa(){
for(int i=1;i<=n;i++){
dis[i]=999999999;
vis[i]=0;
cnt[i]=0;
}
vis[1]=1;
dis[1]=0;
queue<int>q;
q.push(1);
while(!q.empty()){
int now=q.front();
vis[now]=0;
q.pop();
for(int i=head[now];i;i=edge[i].next){
int next=edge[i].to;
if(dis[next]>dis[now]+edge[i].w){
dis[next]=dis[now]+edge[i].w;
cnt[now]++;
if(cnt[now]>=n) return 1;
if(!vis[next]){
vis[next]=1;
q.push(next);
}
}
}
}
return 0;
}
void solve(){
n=read();m=read();
memset(head,0,sizeof(head));
for(int i=1;i<=m;i++){
int x=read(),y=read(),w1=read();
add(x,y,w1);
if(w1>=0) add(y,x,w1);
}
if(spfa()) cout<<"YE5\n";
else cout<<"N0\n";
return;
}
int main(){
int t=read();
for(int i=1;i<=t;i++){
top=0;
solve();
}
return 0;
}