题目描述
【题意】
给出一个图,起始点是1,结束点是N,边是双向的。求点1到点N的最短距离。哈哈,这就是标准的最短路径问题。
【输入格式】
第一行为两个整数N(1≤N≤10000)和M(0≤M≤200000)。N表示图中点的数目,M表示图中边的数目。
下来M行,每行三个整数x,y,c表示点x到点y之间存在一条边长度为c。(x≠y,1≤c≤10000)
【输出格式】
输出一行,一个整数,即为点1到点N的最短距离。
如果点1和点N不联通则输出-1。
【样例1输入】
2 1
1 2 3
【样例1输出】
3
【样例2输入】
3 3
1 2 5
2 3 5
3 1 2
【样例2输出】
2
【样例3输入】
6 9
1 2 7
1 3 9
1 5 14
2 3 10
2 4 15
3 4 11
3 5 2
4 6 6
5 6 9
【样例3输出】
给出一个图,起始点是1,结束点是N,边是双向的。求点1到点N的最短距离。哈哈,这就是标准的最短路径问题。
【输入格式】
第一行为两个整数N(1≤N≤10000)和M(0≤M≤200000)。N表示图中点的数目,M表示图中边的数目。
下来M行,每行三个整数x,y,c表示点x到点y之间存在一条边长度为c。(x≠y,1≤c≤10000)
【输出格式】
输出一行,一个整数,即为点1到点N的最短距离。
如果点1和点N不联通则输出-1。
【样例1输入】
2 1
1 2 3
【样例1输出】
3
【样例2输入】
3 3
1 2 5
2 3 5
3 1 2
【样例2输出】
2
【样例3输入】
6 9
1 2 7
1 3 9
1 5 14
2 3 10
2 4 15
3 4 11
3 5 2
4 6 6
5 6 9
【样例3输出】
20
这就是最基本的最短路问题(caioj.cn1088)
最短路第一步肯定是在输入的时候建边
然后搜索一次,最后输出最优解
一般用数组存,和dp很类似
言归正传,这一道题有2种方法
1:SPFA(宽搜)
a[x][y]记录点x到y的路径(直接,联系,一开始初始化为无限大)
d[x]记录从1到点x的最小距离
v[i]记录点i是否在宽搜队列里面
输入:
scanf("%d%d%d",&x,&y,&c);
if(c<a[x][y]) a[x][y]=c,a[y][x]=c;
初始化:
memset(d,127,sizeof(d));d[1]=0;//先变得无限大,方便以后,点1自己到自己的距离为0
memset(v,false,sizeof(v));v[1]=true;//一开始只有点1在队列里面
主要程序:
memset(d,127,sizeof(d));d[1]=0;//先变得无限大,方便以后,点1自己到自己的距离为0
memset(v,false,sizeof(v));v[1]=true;//一开始只有点1在队列里面
while(head!=tail)
{
x=list[head];//方便很多,不要因为这个出现错误
for(y=1;y<=n;y++)
{
if(a[x][y]<=10000)//题目说<=10000,表明这是一条边
{
if(d[y]>d[x]+a[x][y])//如果以前到达这一个点的最小值比这一种方案要大
{
d[y]=d[x]+a[x][y];//更新
if(v[y]==false)//如果不在队列里面(剪枝),在对列里面的直接更新就可以了
{
v[y]=true;//把它标记为“在”
list[tail]=y;//放进队列里面
tail++;if(tail==n+1) tail=1;//队列尾+1,最后一句是为了循环利用空间,节省内存
}
}
}
}
list[head]=0;//标记为0,以后再用
v[y]=false;//出队列
head++;if(head==n+1) head=1;//头+1
}
如果觉得这样比较慢,我们可以加一个目录,虽然麻烦一点点,可是快了很多
先定义一个结构体
struct node
{
int x,y,d,next;//x和y表示两个点,d表示费用,next表示以x开头的下一条边的编号(最后结束就是next=0,表示下一条没有边)
}a[210000];int len,last[11000];
//len表示边的数量
//last表示以x开头最后一条边的编号
建边函数
inline void ins(int x,int y,int d)
{
len++;
a[len].x=x;a[len].y=y;a[len].d=d;
a[len].next=last[x];last[x]=len;
}
记得建双向边(ins(x,y);ins(y,x))
找边就是:for(int k=last[x];k>0;k=a[k].next)
2、Floyd(3重循环,时间o(n^3))
先定义一个inf(无穷大),把每一条边设置为inf,输入完以后
标准的五行代码:
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
for(int k=1;k<=n;k++)
if(d[i][j]>d[i][k]+d[k][j])
d[i][j]=d[i][k]+d[k][j];
注这个代码时间很长,边多的时候不要用