继续补坑2333.
继续复习ht讲的图论,纠结了两天的spfa和dijkstra堆优化。
首先是纠结关于邻接表的问题。
关键语句只有两句
e[i].next=head[e[i].u];
head[e[i].u]=i;
怎么理解呢?
其实不需要理解233333,其实就是head储存的是点在那条边上,然后不断遍历next,顺次遍历每条边。zhx讲的其实代码背着背着就理解了QAQ。
理解意思之后20分钟手打了一个spfa,发现怎么也过!不!去!
好气,看了一上午的程序,请教邹神,邹神还嫌我弱QAQ,最后发现我一个括号加错了地方= =,还是很开心至少凭借着辣鸡智商看懂了这种算法(๑•ᴗ•๑)。
spfa呢,其实是基于Bellman-Ford的一个优化,大体思路呢,就是设置一个队列,初始将源点添加进队列,每次将队列里的点出队,然后用邻接表或者前向星来遍历这个点所发出的边,用这条边来判断此这条边到达的点是否能继续松弛,如果能继续松弛则将此点入队,如果这个点已经在队里则不管,判断此点如果入队了n次,则此图中有负环。
先附上自己的代码
#include <stdio.h>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
const int maxn=300001;
const int inf =0x7ffffff;
struct edge
{
int from,to,w,next;
}e[1000001];
int head[maxn];
int vis[maxn];
int dist[maxn];
int n,m,t,a,b,c;
void add(int i,int j,int w)//用邻接表储存边
{
e[t].from=i;
e[t].to=j;
e[t].w=w;
e[t].next=head[i];
head[i]=t++;
}
void spfa(int s)
{
queue <int> q;
for(int i=1;i<=n;i++)
dist[i]=inf;
memset(vis,0,sizeof(vis));
q.push(s); //将源点入队
dist[s]=0;
while(!q.empty())
{
int u=q.front();
q.pop(); //出队
vis[u]=0;
for(int i=head[u];i!=-1;i=e[i].next)//遍历以此点为起点的边
{
int v=e[i].to;
if(dist[v]>dist[u]+e[i].w)//松弛
{
dist[v]=dist[u]+e[i].w;
if(!vis[v]) //判断松弛过的点是否在队中
{
vis[v]=1;
q.push(v); //将松弛过的点入队
}
}
}
}
}
int main()
{
freopen("std.in","r",stdin);
freopen("std.out","w",stdout);
int s,e;
scanf("%d%d%d",&n,&m,&s);
t=1;
memset(head,-1,sizeof(head)); //初始化邻接表
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
add(a,b,c);
}
spfa(s);
for (e=1;e<=n;e++)
if(dist[e]==inf) printf("2147483647 ");
else printf("%d ",dist[e]);
return 0;
fclose(stdin);
fclose(stdout);
//用文件输入输出是因为自己程序太长时间没有调出来跟大神程序对拍了一发。
}
关于spfa的优化个人感觉不太想用了,玄学优化,不加优化跑了150ms,加了SLF优化之后跑了250ms。
第二天纠结了一天dijkstra的堆优化,首先复习一发dijkstra的正常写法,思路是把点分成两个集合,不断将将一个集合中的点加入另一个集合并进行松弛。
不详细的回顾了,见代码
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int inf = 1000000000;
int n,m,k,c,p,s,dis[10000],head[10000],vis[10000];
struct edge{
int from,to,w,next;
}e[500000];
void init()//邻接表储存边集
{
cin>>n>>m>>s;
for(int t=1;t<=m;t++)
{
scanf("%d%d%d",&e[t].from,&e[t].to,&e[t].w);
e[t].next=head[e[t].from];
head[e[t].from]=t;
}
}
void dijkstra(int s)
{
for(int t=1;t<=n;t++)
dis[t]=inf;//初值赋为inf。
dis[s]=0;//将源点初值赋为0;
memset(vis,0,sizeof(vis));
for(int t=1;t<=n;t++)
{
int k=0;dis[k]=inf;
for(int i=1;i<=n;i++)//寻找未加入集合的,距离源点距离最小的点
{
if(!vis[i]&&dis[k]>dis[i])k=i;//记录此点
}
vis[k]=1;if (dis[k]==inf)break;//如记录的最小距离是inf,就中止循环
for (int q=head[k];q!=-1;q=e[q].next)//用此点遍历其他各点
{
if (!vis[e[q].to]&&dis[k]+e[q].w<dis[e[q].to])
dis[e[q].to]=e[q].w+dis[k];
}
}
}
int main()
{
//freopen("std.in","r",stdin);
//freopen("dp.out","w",stdout);
memset(head,-1,sizeof(head));
init();
dijkstra(s);
for(int i=1;i<=n;i++)
if (dis[i]==inf)printf("2147483647 ");else printf("%d ",dis[i]);
//fclose(stdin);
//fclose(stdout);
}
实测结果做洛谷的模版题超时了三个,用spfa最大数据在160ms左右
然后就是dijkstra的堆优化啦,跑模版最大数据在200ms左右,但是相对于spfa,dijkstra的堆优化更加的稳定。
个人感觉堆优化之后的dijkstra一点都不像原版的dijkstra啊,感觉跟spfa一样了,此处用stl里的优先队列代替堆,神犇也可以自己写个左偏树啥的,但此处不需要合并之类的操作,果断用优先队列省力气了,noip里一般都不需要自己写堆(坐等试题打脸= =),至于手写堆啥的这些坑等到省选再补把。
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int maxn=100000,inf=1000000000;
int d[maxn];//距离
bool vis[maxn];
vector <int> G[10001];
int n,m,s;
struct edge{ //边
int u,v,w;
}edges[500000];
struct HeapNode{ //点
int d,u;
bool operator < (const HeapNode& rhs)const{
return d>rhs.d;}
};//重载运算符之后可以直接建堆。
void dijkstra(int s)
{
priority_queue<HeapNode>Q;//建堆
memset(d,0x3f,sizeof(d));//初始化距离数组
d[s]=0;//将源点赋值为0;
Q.push((HeapNode){0,s});//入堆
while (!Q.empty())
{
HeapNode x=Q.top();Q.pop();//将距离最短的点出队。
int u=x.u;
if(vis[u])continue;//如果已经被访问过就进入下一次循环。
vis[u]=true;
for (int i=0;i<G[u].size();i++)//遍历此点为起点的所有边
{
edge& e=edges[G[u][i]];
if(d[e.v]>d[u]+e.w)
{
d[e.v]=d[u]+e.w;//松弛
Q.push((HeapNode){d[e.v],e.v});//将松弛过的点入队
}
}
}
}
int main()
{
//freopen("std.in","r",stdin);
//freopen("dp.out","w",stdout);
scanf("%d%d%d",&n,&m,&s);
for (int i=1;i<=m;i++)
{
scanf("%d%d%d",&edges[i].u,&edges[i].v,&edges[i].w);
G[edges[i].u].push_back(i);//以vector为容器,使用前向星存边
}
dijkstra(s);
for (int i=1;i<=n;i++)
{
printf("%d ",d[i]==0x3f3f3f3f?2147483647:d[i]);
}
//fclose(stdin);
//fclose(stdout);
return 0;
}
图论还剩tarjan和lca的坑= =,心好累