题目大意是给你一个图,找出一个点使得到其他点的最短路径和最小,返回这个最小值
第一次觉得是个多源最短路,写了Floyd,超时!
第二次看了正解:枚举每个点,求一次单源最短路,用朴素Dijkstra,超时!
然后学习STL,立志学会优先队列,终于做好了堆优化,但是还是超时!
原来存图用的是邻接矩阵,莫名其妙就会慢(question1/难道是因为每次也要便利所有节点进行松弛的原因吗?求大神解答)
后来改成邻接表存图,终于,,,他过了,他快速的过了……
曾一度想改成SPFA ,却到现在还是迷惑于两者的本质区别(question2/难道是vis数组在Dijkstra里用于记录是否松驰过这个节点的边,而在SPFA里用于记录本次松驰过的点是否在队列中,以便决定是否进队?再次求大神解答)
对比两种算法的运行时间,果然还是SPFA faster一点
1/Dijkstra+priority_queue
/*
ID: 15659801
LANG: C++
TASK: butter
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define OPENFILE(s) freopen(#s".in","r",stdin);freopen(#s".out","w",stdout);//宏定义
#define CLOSEFILE fclose(stdin);fclose(stdout);
#define ll long long
#define inf 1e9+7
using namespace std;
struct Node//定义优先队列,{节点,最小距离},运算符重载实现最小优先队列(默认最大优先队列)
{
int x,d;
int operator<(const Node& r)const
{
return d>r.d;
}
} ;
struct sss //邻接表存图(用邻接矩阵会TLE)
{
int x;
int w;
sss *next;
}gra[801],mem[1501*2];//静态预存节点
int cnt=-1;
int n,p,c;
int phc[801];//记录牧场有几头牛
int dis[801],vis[801];
void creat_edge(int fr,int to,int we)//邻接表存图
{
struct sss *p=&mem[++cnt];
p->x=to;
p->w=we;
p->next=gra[fr].next;
gra[fr].next=p;
}
void scanff()
{
cin>>n>>p>>c;
int i,x;
int fr,to,we;
for(i=0; i<n; ++i)
{
cin>>x;
phc[x]++;
}
for(i=0;i<=p;++i)//预处理邻接表表头
{
gra[i].w=0;
gra[i].next=NULL;
}
for(i=0; i<c; ++i)
{
cin>>fr>>to>>we;
creat_edge(fr,to,we);//存图时注意是有向图还是无向图!!!!
creat_edge(to,fr,we);
}
}
int dijkstra(int s)
{
for(int i=1; i<=p; ++i)
dis[i]=inf;
memset(vis,0,sizeof(vis));//vis的意义等待大神们的官方解答
dis[s]=0;
priority_queue<Node > pq; //定义优先队列
pq.push((Node){s,dis[s]});
while(!pq.empty())//Dijkstra核心算法
{
int pn=pq.top().x;//每次选出最短路径估计最小的点
pq.pop();
if(vis[pn]==1)
continue;
vis[pn]=1;
struct sss *pi=gra[pn].next;
for(;pi!=NULL;pi=pi->next)//遍历当前节点可以直接到达的所有节点
{
if(vis[pi->x]==0&&dis[pn]+pi->w<dis[pi->x])
{
dis[pi->x]=dis[pn]+pi->w;//进行松弛操作
pq.push((Node)
{
pi->x,dis[pi->x] //并压入栈中
});
}
}
}
int ans=0;//统计最短路径和
for(int i=1; i<=p; ++i)
if(phc[i])
ans+=phc[i]*dis[i];
return ans;
}
int main()
{
OPENFILE(butter);
scanff();
int i,mind=inf,ddd;
for(i=1; i<=p; ++i)
{
ddd=dijkstra(i);
if(ddd<mind)//记录最小值
mind=ddd;
}
cout<<mind<<endl;
CLOSEFILE;
return 0;
}
2/SPFA
/*
ID: 15659801
LANG: C++
TASK: butter
*/
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
#include <queue>
#define OPENFILE(s) freopen(#s".in","r",stdin);freopen(#s".out","w",stdout);//宏定义
#define CLOSEFILE fclose(stdin);fclose(stdout);
#define ll long long
#define inf 1e9+7
using namespace std;
struct sss //邻接表存图(用邻接矩阵会TLE)
{
int x;
int w;
sss *next;
} gra[801],mem[1501*2]; //静态预存节点
int cnt=-1;
int n,p,c;
int phc[801];//记录牧场有几头牛
int dis[801],vis[801];
void creat_edge(int fr,int to,int we)//邻接表存图
{
struct sss *p=&mem[++cnt];
p->x=to;
p->w=we;
p->next=gra[fr].next;
gra[fr].next=p;
}
void scanff()
{
cin>>n>>p>>c;
int i,x;
int fr,to,we;
for(i=0; i<n; ++i)
{
cin>>x;
phc[x]++;
}
for(i=0; i<=p; ++i) //预处理邻接表表头
{
gra[i].w=0;
gra[i].next=NULL;
}
for(i=0; i<c; ++i)
{
cin>>fr>>to>>we;
creat_edge(fr,to,we);//存图时注意是有向图还是无向图!!!!
creat_edge(to,fr,we);
}
}
int spfa(int s)
{
for(int i=1; i<=p; ++i)
dis[i]=inf;
memset(vis,0,sizeof(vis));//vis的意义等待大神们的官方解答
dis[s]=0;
queue <int> pq; //定义队列
pq.push(s);
vis[s]=1;//表示s点在队列中
while(!pq.empty())//SPFA核心算法
{
int pn=pq.front();//弹出队首元素
pq.pop();
vis[pn]=0;//表示队首元素已经出队
struct sss *pi=gra[pn].next;
for(; pi!=NULL; pi=pi->next) //遍历当前节点可以直接到达的所有节点
if(dis[pn]+pi->w<dis[pi->x])
{
dis[pi->x]=dis[pn]+pi->w;//进行松弛操作
if(vis[pi->x]==0) //如果松弛了而且该点不在队列中,就压入队列
{
pq.push(pi->x);
vis[pi->x]=1;//标记该元素已经入队
}
}
}
int ans=0;//统计最短路径和
for(int i=1; i<=p; ++i)
if(phc[i])
ans+=phc[i]*dis[i];
return ans;
}
int main()
{
OPENFILE(butter1);
scanff();
int i,mind=inf,ddd;
for(i=1; i<=p; ++i)
{
ddd=spfa(i);
if(ddd<mind)//记录最小值
mind=ddd;
}
cout<<mind<<endl;
CLOSEFILE;
return 0;
}