http://acm.hdu.edu.cn/showproblem.php?pid=2066
一开始想运用跟之前soj的办法,就是把每个起点都进行一次dijkstra,然后记录下每个顶点下的最短路。。后来发现其实对于车站可以看做与起点之间有一条权值为0的边,那么这样就可以直接使用单源最短路算法了。
0、要注意的就是这里并没有给出顶点的个数,只知道上限是1000,就要记录下顶点的最大的序号。
1、是无向图要记得双向的权值都是一样的。
2、用一个数组存储终点,最后遍历终点集合,找出最小的那个。
学习了一下dijkstra的优先队列优化,总结一下,用一个struct edge来存储边的信息(包括终点,以及权值),用一个vector 来存储每个顶点指向的边的信息记为 G[ ],G[i]表示以i为起点的边的信息。用pair<int,int> 来表示一对最短距离和顶点的编号。
这个优化的重点就是用优先队列取出每次最短距离最小的点,如果当前的最小值并不是最短距离就丢弃。在遍历与这个点相连的所有点,利用已知最短距离更新其他点的当前最短距离。
优先队列的优化:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
#include <queue>
using namespace std;
#define M 1009
#define INF 0x3f3f3f3f
int t,s,d,n;
struct edge
{
int to,w;
};
vector<edge> G[M];
typedef pair<int,int> P;//first表示距离,second 表示的是顶点的编号
int dis[M];
int ed[M];
void dijkstra()
{
priority_queue<P,vector<P>,greater<P> > q; //使用优先队列进行优化
fill(dis,dis+M,INF);
dis[0] = 0;
q.push(P(0,0));
while(!q.empty()) //
{
P p = q.top(); //找到的最小距离的边
q.pop();
int u = p.second; //拥有最小距离的边的顶点
if(dis[u] < p.first) continue; //如果该顶点的最小距离已经小于当前的最小距离 (取出的最小值不是最短距离就丢弃)
for(int i = 0;i < G[u].size();i++) //取出所有与这个边相连的边 更新最小距离
{
edge e = G[u][i];
if(dis[e.to] > dis[u] + e.w)
{
dis[e.to] = dis[u]+e.w;
q.push(P(dis[e.to],e.to)); //往堆里插入当前最短距离和顶点的值对
}
}
}
}
int main()
{
while(scanf("%d %d %d",&t,&s,&d)==3)
{
memset(ed,0,sizeof(ed));
n = 0;
for(int i = 0;i < M;i++)
G[i].clear();//要放在赋值之前进行初始化orzz
for(int i = 0;i < t;i++)
{
int a,b,c;
edge e;
scanf("%d %d %d",&a,&b,&c);
if(n<a) n = a;
if(n<b) n = b;
e.to = b; e.w = c;
G[a].push_back(e);
e.to = a;
G[b].push_back(e); //无向图 双向的路径
}
n++;
for(int i = 0;i < s;i++)
{
int a;
scanf("%d",&a);
edge e;
e.to = a;e.w = 0;
G[0].push_back(e); //把车站看成与起点之间存在一条权值为0的边,这样就可以轻松的使用单源最短路解决
e.to = 0;
G[a].push_back(e);
}
for(int i = 0;i < d;i++)
{
scanf("%d",&ed[i]);
}
dijkstra();
int minn = INF;
for(int i = 0;i < d;i++)
{
minn = min(minn,dis[ed[i]]);//找出最小的距离
}
printf("%d\n",minn);
}
return 0;
}
未优化的朴素写法:
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define M 1009
#define INF 0x3f3f3f3f
int map[M][M];
int dis[M];
int vis[M];
int ed[M];
int t,s,d;
int n;
void dijkstra()
{
for(int i = 0;i <= n;i++)
dis[i] = map[0][i];
dis[0] = 0;
vis[0] = 1;
for(int i = 1;i <= n;i++)
{
int min = INF;
int k;
for(int j = 1;j <= n;j++) //找出离起点最近的点(花费大部分时间) 可以用优先队列进行优化
{
if(!vis[j] && dis[j]<min)
{
min = dis[j];
k = j;
}
}
if(min == INF) return ;
vis[k] = 1;
for(int j = 1;j <= n;j++)//利用已经找到的最近的点k更新最小距离还不确定的点当前的最小距离
{
if(!vis[j] && dis[j]>dis[k]+map[k][j])
dis[j] = dis[k]+map[k][j];
}
}
}
int main ()
{
while(scanf("%d %d %d",&t,&s,&d)==3)
{
memset(vis,0,sizeof(vis));
for(int i = 0;i < M;i++)
for(int j = 0;j < M;j++)
map[i][j] = INF;
n = -INF;
for(int i = 0;i < t;i++)
{
int a,b,c;
scanf("%d %d %d",&a,&b,&c);
if(n<a) n = a; //找出点的个数
if(n<b) n = b;
if(c<map[a][b])
map[a][b]=map[b][a]=c;//无向图
}
for(int i = 0;i < s;i++)
{
int a;
scanf("%d",&a);
map[0][a]=map[a][0]=0;
}
for(int i = 0;i < d;i++)
{
scanf("%d",&ed[i]);
}
dijkstra();
int minn = INF;
for(int i = 0;i < d;i++)
{
if(minn > dis[ed[i]])
minn = dis[ed[i]]; //找出最小的距离
}
printf("%d\n",minn);
}
}