hdoj 1874 通畅工程续 (最短路)

Problem Description
某省自从实行了很多年的畅通工程计划后,终于修建了很多路。不过路多了也不好,每次要从一个城镇到另一个城镇时,都有许多种道路方案可以选择,而某些方案要比另一些方案行走的距离要短很多。这让行人很困扰。

现在,已知起点和终点,请你计算出要从起点到终点,最短需要行走多少距离。
 

Input
本题目包含多组数据,请处理到文件结束。
每组数据第一行包含两个正整数N和M(0<N<200,0<M<1000),分别代表现有城镇的数目和已修建的道路的数目。城镇分别以0~N-1编号。
接下来是M行道路信息。每一行有三个整数A,B,X(0<=A,B<N,A!=B,0<X<10000),表示城镇A和城镇B之间有一条长度为X的双向道路。
再接下一行有两个整数S,T(0<=S,T<N),分别代表起点和终点。
 

Output
对于每组数据,请在一行里输出最短需要行走的距离。如果不存在从S到T的路线,就输出-1.
 

Sample Input
  
  
3 3 0 1 1 0 2 3 1 2 1 0 2 3 1 0 1 1 1 2
 

Sample Output
  
  
2 -1

/*dijkstra算法:
其实它长得和prime算法非常像,不同的是,prime算法里每次更新的lowcost集合是红点集与蓝点集
之间的最小权值路径,然后将这些最小路径里再找最小的一个加入ans,最后的这个ans就是生成树的权值。
而dijkstra里,dis更新的是从开始点到其他每个点的最短长度。
开始时dis先初始化为无穷大,然后从开始点出发,找出一个权值最小的路,将其对应的点标记为vis=1
(第一次循环时这个点必定是开始点),换句话说就是把这个点加入了红点集。
每次找的新点:看看是从原先的路过去近,还是经过红点集中的点再过去近。如果后者,就更新dis,
找出dis中最小的,对应的点就是新点。
之前学数据机构模拟手算的时候我还有个疑问,就是假如红点集中的点是1,2,3,那么我找到4的距离的时候,
是比较从1到4还是从1到2到4还是从1到2到3到4呢?这样的话点越多就越不好判断了呀。
其实算法中,dis是一直更新的,保存的不一直是1-4距离,而是每加入一个点,就看看是不是更短。
例如:加入2之后,就比较1-4和1-2-4,发现后者更小,那么dis就变成了1-2-4.又加入了3,就比较
1-2-4(就是此时的dis)和1-2-3-4.也就是看dis[j]与dis[k]+map[k][j](这里的j为当前考察的点,即4,
k是刚刚加入红点集的新点),所以dis保存的一直是已存所有路径中最短的。这里的道理和lowcost一样。*/
#include <stdio.h>
#include <string.h>
#define INF 0xfffffff
int map[210][210], vis[210], s, t, n, m, dis[210];
int dijkstra()
{
    int i, j, k, min = INF;
    dis[s] = 0;
    for(i = 0 ; i < n ; i++)
    {
        min = INF;
        for(j = 0 ; j < n ; j++)
        {
            if(!vis[j] && min > dis[j])
            {
                min = dis[j];
                k = j;
            }
        }
        vis[k] = 1;
        for(j = 0 ; j < n ; j++)
        {
            if(!vis[j] && dis[j] > (dis[k] + map[k][j]))//比较dis
                dis[j] = dis[k] + map[k][j];
        }
    }
    if(!vis[t])
        return -1;
    return dis[t];
}
int main()
{
    int i, j, a, b, x;
    while(~scanf("%d %d", &n, &m))
    {
        for(i = 0 ; i < n ; i++)
	    {
	        dis[i] = INF;//之前忘了把最短距离先初始化为无穷大
	        for(j = 0 ; j < n ; j++)
                map[i][j] = INF;
	    }
		memset(vis, 0, sizeof(vis));
        for(i = 0 ; i < m ; i++)
        {
            scanf("%d %d %d", &a, &b, &x);
            if(x < map[a][b])
                map[a][b] = map[b][a] = x;
        }
        scanf("%d %d", &s, &t);
        printf("%d\n", dijkstra());
    }
    return 0;
}
//bellman-ford:
#include <stdio.h>
#include <string.h>
#define INF 0xfffffff
typedef struct
{
    int u, v, cost;
}node;
node edge[10050];
int s, t, n, m, dis[210];
int ford()
{
    int i, j;
    dis[s] = 0;
    for(i = 0 ; i < n-1 ; i++)
        for(j = 0 ; j < 2*m ; j++)
    {
        if(dis[edge[j].v] > dis[edge[j].u] + edge[j].cost)
            dis[edge[j].v] = dis[edge[j].u] + edge[j].cost;
    }
    if(dis[t] == INF)
        return -1;
    return dis[t];
}
int main()
{
    int i;
    while(~scanf("%d %d", &n, &m))
    {
        for(i = 0 ; i < n ; i++)
        {
            dis[i] = INF;//之前忘了把最短距离先初始化为无穷大
        }
        for(i = 0 ; i < m ; i++)
        {
            scanf("%d %d %d", &edge[i].u, &edge[i].v, &edge[i].cost);
            edge[m+i].v = edge[i].u;
            edge[m+i].u = edge[i].v;
            edge[m+i].cost = edge[i].cost;
        }
        scanf("%d %d", &s, &t);
        printf("%d\n", ford());
    }
    return 0;
}


问题描述

给定一个n个顶点,m条边的有向图(其中某些边权可能为负,但保证没有负环)。请你计算从1号点到其他点的最短路(顶点从1到n编号)。

输入格式

第一行两个整数n, m。

接下来的m行,每行有三个整数u, v, l,表示u到v有一条长度为l的边。

输出格式
共n-1行,第i行表示1号点到i+1号点的最短路。
样例输入
3 3
1 2 -1
2 3 -1
3 1 2
样例输出
-1
-2
数据规模与约定

对于10%的数据,n = 2,m = 2。

对于30%的数据,n <= 5,m <= 10。

对于100%的数据,1 <= n <= 20000,1 <= m <= 200000,-10000 <= l <= 10000,保证从任意顶点都能到达其他所有顶点。

/*spfa算法:形式和广搜很像。
虽然效率不如dijkstra高,但它可以处理有负权的情况。(dijkstra在边有负权的情况下求出来的最短路是错误的)
哎呀,这种方法在我第一次见到最短路并且当时还不知道dij算法的时候就想到过了,只是没有付诸实施……
不过我当时只想到了邻接矩阵的算法,就是打一个邻接矩阵,像走迷宫一样广搜。搜到一个点就跳到其对应的行上继续搜。
这里由于n太大,所以邻接矩阵不可行,得用邻接表存储。这里的邻接表存储是模拟了链表,用head数组表示
从同一个点出发的不同的边连成的线性表。如head[2]:2-3->2-4->-1。其中head数组的下标是谁就是这个表的
起边都是谁,如上边那个例子就是这个线性表的起边都是2,但终边不相同。head的内容是保存了当前线性表的头结点,
在这里是2-3边的序号。
计算过程就是从起点开始,选择它所在的那一条线性表,逐个松弛。其中能松弛的,就说明这条路被更新了,
那么其对应的终点就要入队,意思是到这条路终点的那一段路被更新得更小了,所以这个终点连着的其它点
可能也要被更新。
这里需要注意的一点就是标记。Bellman-Ford算法中没有这一点,不管三七二十一都进行判断松弛操作。而这里
是对它进行了改进,防止了多余的判断松弛。(松弛操作是都有的,能松就松,但松了以后不一定再入队了)
例如:2-4->2-3->-1, 3-4->-1。松弛了2-4,把4入了队。再下一个3-4中,也进行了松弛,但这里的4就不需要入队了。
防止了多余的操作。出队的点可以再入队。所以每出一个点,就把vis改回0.*/
#include <stdio.h>
#include <queue>
#include <string.h>
#define l 20005
#define inf 0xfffffff
using namespace std;
struct e
{
    int x, y, w, next;
}edge[l*10];
int n, m, head[l], vis[l], dis[l];
queue <int> q;
void spfa(int choose)
{
    int u, v, i;
    vis[choose] = 1;
    dis[choose] = 0;
    q.push(choose);
    while(!q.empty())
    {
        u = q.front();
        q.pop();
        vis[u] = 0;//出队的点标记回未访问。只有出过队的点才有机会再入队。所以标记数组vis必须有。
        for(i = head[u] ; i != -1 ; i = edge[i].next)
        {
            v = edge[i].y;
            if(dis[v] > dis[u] + edge[i].w)
            {
                dis[v] = dis[u] + edge[i].w;
                if(!vis[v])
                {
                    q.push(v);
                    vis[v] = 1;
                }
            }
        }
    }
}
int main()
{
    int i, x, y, w;
    scanf("%d %d", &n, &m);
    for(i = 1 ; i <= n ; i++)
    {
        dis[i] = inf;
        head[i] = -1;//head[i]表示从点i出去的最后那一条边的序号。
        vis[i] = 0;
    }
    for(i = 0 ; i < m ; i++)
    {
        scanf("%d %d %d", &x, &y, &w);
        edge[i].x = x;
        edge[i].y = y;
        edge[i].w = w;
        edge[i].next = head[x];//模拟链表。head数组的下标表示是属于哪个点的那条串,内容是上一次的属于这条串的边的序号。
        head[x] = i;//移动头结点为新的序号。
    }
    spfa(1);
    for(i = 2 ; i <= n ; i++)
        printf("%d\n", dis[i]);
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值