给你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)
输出:
输出 一行有两个数, 最短距离及其花费。
样例输入:
3 2
1 2 5 6
2 3 4 5
1 3
0 0
样例输出:
9 11
二.题目分析
本题是典型的最短路径问题,可以使用单源的Dijkstra算法。
需要提醒的地方是有两处,一是注意题中的要求,在出现多条最短路径时,选择代价最小的一条。满足此要求,需要改进两个地方,在输入时,有可能出现两个点v1,v2之间两条道路,距离相等,但是代价不等,因此输入时需要做代价判断更新(看了题解时才想到这点);另外,在在更新dist数组时,不仅在出现更短距离时需要更新,同时当距离相等时,出现更小代价时也需要更新,这点很容易想到。
二是注意最大值的设定,题中并没有给出最大值的上限,因此我们需要考虑int型的最大值作为INF(本代码中是maxint),int型是4个字节,即32位,有符号范围的最大值是0x7fffffff,map[i][j]用此最大值作为上限需要格外小心,因为在更新dist数组中一旦做加法便会溢出,解决方法有两个,可以采用先判断在做加法的方法,判断map[i][j]<INF是否成立,当map[i][j]=INF时,其实并不需要在继续加了,它本身就是去穷大了,不需要更新数组;或者在加法的时候将map[i][j]中的值先转换成64位的变量,之后再做加法,以下分别会给出代码。
注:在Windows的内存管理机制中,进程的所有使用的地址都是虚拟地址,进程也是运行在虚拟内存中的,虚拟内存的地址和物理内存中的地址通过页表进行转换。在32位Windows中,能够表示的最大寻址范围就是4G,每一个进程都独享4G的虚拟内存。而Windows采取是这样子分配的:将4G空间分成两部分,低2G的部分是给用户使用的,而高2G的部分是保留给系统使用的。这样子,每一个进程真正能够使用的内存就只有2G。而这个2G,包括了栈和堆,以及存放常量和静态数据的区域。2G 内存大小限制是Windows(32位)的限制搜索,是没有办法超越的。
三.代码
#include <stdio.h>
#include <stdlib.h>
#define MAX 1024
#define maxint 0x7fffffff //定义int型最大值
int e,v,map[MAX][MAX],cost[MAX][MAX];
void Dijkstra(int start,int *dist,int *co)
{
int i,j,k,minl,minu;
int visit[MAX];
//init
for(i=0;i<v;i++)
{
dist[i]=map[start][i];
co[i]=cost[start][i];
visit[i]=0; //所有的点未加入S集合中
}
visit[start]=1; //初始点加入S集合中
dist[start]=0;
//find min dist
for(i=1;i<v;i++) //将剩下n-1个点也依次选取最小的dist加入S集合中
{
minl=maxint;
for(j=0;j<v;j++) //选取最小的dist
{
if(!visit[j]&&dist[j]<minl)
{
minu=j;
minl=dist[j];
}
}
//visit j
visit[minu]=1;
//update dist
for(j=0;j<v;j++)
{
if(!visit[j]&&(map[minu][j]<maxint)&&(dist[minu]+map[minu][j]<dist[j]))
{
dist[j]=dist[minu]+map[minu][j];
co[j]=co[minu]+cost[minu][j];
}
else if(!visit[j]&&(map[minu][j]<maxint)&&(dist[minu]+map[minu][j]==dist[j])&&(co[minu]+cost[minu][j]<co[j])) //多条最短距离,更新最小代价
co[j]=co[minu]+cost[minu][j];
/* 网上看到的解法
__int64 temp1=p[xx].dis+c[xx][yy].dis;
__int64 temp2=p[xx].cost+c[xx][yy].cost;
if(!vis[yy]&&(temp1<p[yy].dis||(temp1==p[yy].dis&&temp2<p[yy].cost)))
{
p[yy].dis=temp1;
p[yy].cost=temp2;
}
*/
}
}
}
int main()
{
int i,j,v1,v2,start,end,p1,p2;
int dist[MAX],co[MAX];
freopen("1008.txt","r",stdin);
while(scanf("%d%d",&v,&e)&&(v||e))
{
//init
for(i=0;i<v;i++)
{
for(j=0;j<v;j++)
{
map[i][j]=maxint;
cost[i][j]=maxint; //add
}
}
//input
for(i=0;i<e;i++)
{
scanf("%d%d%d%d",&v1,&v2,&p1,&p2);
if(map[v1-1][v2-1]>p1)
{
map[v1-1][v2-1]=p1;
map[v2-1][v1-1]=p1;
cost[v1-1][v2-1]=p2;
cost[v2-1][v1-1]=p2;
}
else if(map[v1-1][v2-1]==p1&&cost[v1-1][v2-1]>p2) //输入在距离相等时,更新最小的代价
{
cost[v1-1][v2-1]=p2;
cost[v2-1][v1-1]=p2;
}
}
scanf("%d%d",&start,&end);
Dijkstra(start-1,dist,co); //起点,距离数组,代价数组
printf("%d %d\n",dist[end-1],co[end-1]);
}
return 0;
}