求两点的最短路径
题目描述
给你n个点,m条无向边,每条边都有长度d和花费p,给你起点s终点t,要求输出起点到终点的最短距离及其花费,如果最短距离有多条路线,则输出花费最少的。
输入描述:
输入n,m,点的编号是1~n,然后是m行,每行4个数 a,b,d,p,表示a和b之间有一条边,且其长度为d,花费为p。最后一行是两个数 s,t;起点s,终点t。n和m为0时输入结束。
(1<n<=1000, 0<m<100000, s != t)
输出描述:
输出 一行有两个数, 最短距离及其花费。
示例1
输入
3 2
1 2 5 6
2 3 4 5
1 3
0 0
输出
9 11
思路分析
一道典型的Dijkstra问题,求两点之间的最短路径
1、需要邻间表,使用vector储存
2、把所有点到源点s的距离记录在dis数组里,把花费记录在cost数组里。
3、思路:Dijkstra简单来说就是定义两个集合S和T,S代表源点,T代表剩余的点,每次都把T中到源点最小距离的点加入S,更新dis数组和cost数组即可。比如0–1距离1,0–2距离5,1–2距离2,求0到2的最短路径。首先把01,02遍历,记录dis[1]=1和dis[2]=5,此时T中距离源点0最小距离的点是1,把1加入S,然后从1开始遍历与它相邻的边,发现12相连,则此时的话到2有两条路径,0-1-2和0-2,只需要比较dis[1]+2与dis[2]的大小,取小的即是0-2的最短路径。花费则同理。
如何每次都取出T中距离源点最小距离的点呢?每次求完都排序一次吗?
其实可以使用优先队列,将集合T中的定点到源点的距离作为这些点的优先级
因此我们需要
两个结构体
struct Edge{
int length;
int to; //不需要from是因为 邻间表的下标就是from
int price;
Edge(int t,int len,int p):to(t),length(len),price(p){}
};
struct Point{
int distance;//该点到源点的距离
int number; //当前编号
Point(int d,int n):distance(d),number(n){ }
bool operator< (const Point& p)const{
return distance>p.distance; //距离最近优先级最大
}
};
邻间表的存储
vector<Edge> graph[MAXN];
******主函数内
while(m--) //填充邻间表
{
int from,to,length,price;
scanf("%d%d%d%d",&from,&to,&length,&price);
graph[from].push_back(Edge(to,lenght,price)); //无向图,两个点都需要加
graph[to].push_back(Edge(from,length,price));
}
Dijkstra算法
void Dijkstra(int s) //把所有点到s的距离记录在dis数组中
{
priority_queue<Point> myQ;
dis[s]=0;
cost[s]=0;
myQ.push(Point(dis[s],s)); //压入一个点
while(!myQ.empty()) //当队列不空
{
Point current=myQ.top(); //取出优先级最高的点
int u=current.number; //点的编号
myQ.pop();
for(int i=0;i<graph[u].size();i++) //遍历与该点相连的所有点,更新dis和cost数组
{
int v=graph[u][i].to;
int d=graph[u][i].length;
int p=graph[u][i].price;
if(dis[v]>dis[u]+d||(dis[v]==dis[u]+d&&cost[v]>cost[u]+p)) //判断条件 ----通过点u是否使得从源点到v的距离缩短
{
dis[v]=dis[u]+d;
cost[v]=cost[u]+p;
myQ.push(Point(dis[v],v));
}
}
}
}
大功告成—
最后写几个注意的地方
1、在hdu上提交时超时,找了半天不知道错误在哪,后来发现是c++的cin函数的关系,所以以后这种题目都用scanf
2、填充函数
fill(dis,dis+n,INF),把dis数组全部初始化成最大值
memset(graph,0,sizeof(graph)), 把graph数组初始化为0 ----头函数#include<string.h> 牛客网会报错。。
一般来说,初始化0使用memest,fill可以无限制使用,前提得知道初试地址和末地址
完整代码
#include<iostream>
#include<algorithm>
#include<string>
#include<vector>
#include<cmath>
#include<queue>
#include<climits>
using namespace std;
const int MAXN=1001;
const int INF=INT_MAX;
struct Edge{
int length;
int to;
int price;
Edge(int t,int len,int p):to(t),length(len),price(p){
}
};
struct Point{
int distance;//该点到源点的距离
int number; //当前编号
Point(int d,int n):distance(d),number(n){ }
bool operator< (const Point& p)const{
return distance>p.distance; //距离最远优先级最小
}
};
vector<Edge> graph[MAXN];
int dis[MAXN];
int cost[MAXN];
void Dijkstra(int s) //把所有点到s的距离记录在dis数组中
{
priority_queue<Point> myQ;
dis[s]=0;
cost[s]=0;
myQ.push(Point(dis[s],s));
while(!myQ.empty())
{
Point current=myQ.top(); //取出离s最近的点
int u=current.number;
myQ.pop();
for(int i=0;i<graph[u].size();i++)
{
int v=graph[u][i].to;
int d=graph[u][i].length;
int p=graph[u][i].price;
if(dis[v]>dis[u]+d||(dis[v]==dis[u]+d&&cost[v]>cost[u]+p))
{
dis[v]=dis[u]+d;
cost[v]=cost[u]+p;
myQ.push(Point(dis[v],v));
}
}
}
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)!=EOF)
{
if(!m&&!n) break;
fill(dis,dis+n+1,INF);
fill(cost,cost+n+1,INF);
memset(graph,0,sizeof(graph));
while(m--) //填充邻间表
{
int A,B,X,P;
scanf("%d%d%d%d",&A,&B,&X,&P);
graph[A].push_back(Edge(B,X,P));
graph[B].push_back(Edge(A,X,P));
}
int s,t;
scanf("%d%d",&s,&t);
Dijkstra(s);
printf("%d %d\n",dis[t],cost[t]);
}
return 0;
}