UVA1599带权图的最短路径以及边的保存方式

这道题目我一开始的想法是还是按照保存父节点的基本思路,但是每次的时候需要挑选每个节点中权值最小的那条边对应的那个点

但是这样会有问题:

第一个问题是:如果某个节点有两条边的权值都是一样的,那么这两个对应的点都必须要入队,那么问题就出现了,如果这俩个节点对应的下一个节点是同一个节点,但是先入队的那个的对应边的权值比较大,后入队的那条边对应的权值比较小,那么在前一个节点访问设置vis后,后面那个节点就不可以再访问了。

其实上面这个情况还是最简单的情况,还有很多更复杂的情况没有考虑到。

所以需要使用刘汝佳的方式:

第一步,先逆向BFS,将每一个点到终点的距离都找到,并放入d[]数组当中;这样一来,每一条最短路径在d数组中对应的距离都是按照-1开始递减的。

第二步,从起点开始BFS,每次都寻找d中比当前点小1的那个点,并且要边的权值最小,如果有多条边的权值都是最小值,那么这几个点需要一起判断。

下面重点来讲解一下第二步的实现方式:

从起点开始,先遍历一下所有d比起点小1的那些点,找到最小的权值的边,然后用这个权值来寻找对应的点,并将这些点放入next《vector》中,这个数组中的点就是下次需要判断的点。所以需要设立两个next《vector》.中途可以将最短的权值 的边保存起来,便于输出。

要注意的是,必须要遍历两次,因为第一次遍历的时候并不知道权值最小的边是哪一条。第二点是,第二次遍历的时候需要设置vis,虽然只有最短边的d数组是符合条件的,所以不加vis也是符合要求的,但是考虑一种情况,那就是两个点对应的下一个点是同一个点,那么这样同一个点就会进入next《vector》两次,而实际上只有需要进入一次。所以vis不仅可以保证正确性,还可以保证访问的效率。

其次有必要说一说这个题目中边的保存方式:

一改往日的数组保存,在这种节点特别多的情况下,将每一个点的边(结构体)保存到G[i]中,也是一个不错的选择

最后来说说vc的使用:

今天发现了两个,第一个是min函数需要变成__min, long long 需要变成__int64;

还有就是并列的for循环,如果在一开始定义变量的话(int;;),那么是不可以重复的。

下面附上AC代码

//这题就是最短路径的变形,主要有两点
//第一点就是在两个点有多条道路的时候,选一条最小的
//第二点就是最后可能有多条路径,从其中选择字典序最小的很容易,但是我怎么知道那一条是最短的呢?
//明白了,一条一条比较就ok

//上面这种方式可能比较的麻烦一点,首先记录父节点就是一个麻烦事//而且记录完父节点还需要去把所有的最短路都计算一遍
//最后的比较也不怎么好搞,因为是倒着比较的,这吉尔太麻烦了,其实这两种方式都可以,我觉得我的方式更具有普遍性一点

//从终点倒着BFS求最短路径的时候,一定是和正着的时候一样的,因为最短路径的长度是不变的。

//此题刘汝佳的解法不怎么具有普遍性,因为它只能保存边,而不能得到点,如果想要得到点也可以再继续计算一下就可以了

//但它的这个next的方式非常的好

//保存图的方式也有了变化,不再使用数组保存,而开始使用结构体了,所有的边都保存在一个vector中,然后每一个g[vec]中保存边的编号

#include<cstdio>
#include<iostream>
#include<queue>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;

const int maxn = 100000 + 10;

int num_v,num_e;
struct edge
{
    int v,c;
    edge(int a = 0, int b = 0):v(a),c(b){}
};

vector<edge>G[maxn];
int vis[maxn];
int d[maxn];

int main()
{
#ifdef local
    freopen("input.txt","r",stdin);
#endif
    while(scanf("%d%d",&num_v,&num_e) == 2)
    {
        for(int i_0 = 1; i_0 <= num_v; i_0++) G[i_0].clear();
        memset(vis,0,sizeof(vis));
        memset(d,0,sizeof(d));
        for(int i_1 = 1; i_1 <= num_e; i_1++)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            G[a].push_back(edge(b,c));
            G[b].push_back(edge(a,c));
        }
        queue<int>Q;
        Q.push(num_v);
        vis[num_v] = 1;
        d[num_v] = 0;
        //d[]保存所有的距离,但并不是每一个点都有这个距离
        while(!Q.empty())
        {
            int x = Q.front();
            if(x == 1)
                break;
            Q.pop();
            for(int i_2 = 0; i_2 < G[x].size();i_2++)
            {
                int v = G[x][i_2].v;
                if(!vis[v])
                {
                    vis[v] = 1;
                    d[v] = d[x] + 1;
                    Q.push(v);
                }

            }
        }
        memset(vis ,0,sizeof(vis));
        vis[1] = 1;
        vector<int>next;
        vector<int>color;
        next.push_back(1);
        for(int i_2 = 1;i_2 <= d[1];i_2++)
        {
            int min_color = 1000000000;
            for(int j = 0; j < next.size();j++)
            {
                int x = next[j];
                for(int j_2 = 0;j_2 < G[x].size();j_2++)
                {
                    if(d[G[x][j_2].v] == d[x] - 1)
                    {
                        min_color = __min(min_color,G[x][j_2].c);
                    }

                }
            }
            color.push_back(min_color);
            vector<int>next2;
            for(int j_1 = 0;j_1 < next.size();j_1++)
            {
                int x = next[j_1];
                for(int j_2 = 0;j_2 < G[x].size();j_2++)
                {
                    if(d[G[x][j_2].v] == d[x] - 1 && G[x][j_2].c == min_color && !vis[G[x][j_2].v])//就是不知道应不应该判断vis???
                    {
                        vis[G[x][j_2].v] = 1;//判重的作用不仅是为了提高正确率,还可以减少无谓的时间消耗
                        next2.push_back(G[x][j_2].v);
                    }

                }
            }
            next = next2;
        }
        printf("%d\n",d[1]);
        for(int i = 0; i < color.size();i++)
        {
            if(i)
                printf(" ");
            printf("%d",color[i] );
        }
        printf("\n");

    }
    return 0;
}

原文地址:https://www.cnblogs.com/TorettoRui/p/10459790.html

时间: 2024-11-05 20:43:34

UVA1599带权图的最短路径以及边的保存方式的相关文章

带权图的最短路径算法(Dijkstra)实现

一,介绍 本文实现带权图的最短路径算法.给定图中一个顶点,求解该顶点到图中所有其他顶点的最短路径 以及 最短路径的长度.在决定写这篇文章之前,在网上找了很多关于Dijkstra算法实现,但大部分是不带权的.不带权的Dijkstra算法要简单得多(可参考我的另一篇:无向图的最短路径算法JAVA实现):而对于带权的Dijkstra算法,最关键的是如何“更新邻接点的权值”.本文采用最小堆主辅助,以重新构造堆的方式实现更新邻接点权值. 对于图而言,存在有向图和无向图.本算法只需要修改一行代码,即可同时实

有向无环带权图的最短路径及长度

给定一个有向无环图的拓扑序列,获取这个序列从起点到序列最后一点的最短路径. 起点默认为0点(顶点为0,1,2...和数组索引对应),序列通过拓扑排序获取. 下面给出实现,首先是对一个有向无环图进行拓扑排序的类. package graphics.dag.topologicalsort; /** * 获取一个拓扑序列 * @author zhangxinren * */ public class TopologicalSort { // 每条边的入度 private static int[] in

Java数据结构——带权图

带权图的最小生成树——Prim算法和Kruskal算法 带权图的最短路径算法——Dijkstra算法 package graph; // path.java // demonstrates shortest path with weighted, directed graphs 带权图的最短路径算法 // to run this program: C>java PathApp ////////////////////////////////////////////////////////////

用无向带权图实现校园导航系统

学校数据结构的课程实验之一. 用到的数据结构:无向带权图 用到的算法:Floyd最短路径算法,深度优先搜索(递归实现) 需求分析: 设计一个校园导航程序,为访客提供各种信息查询服务: 1. 以图中各顶点表示校内各单位地点,存放单位名称,代号,简介等信息:以边表示路径,存放路径长度等相关信息. 2. 图中任意单位地点相关信息的查询. 3. 图中任意单位的问路查询,即查询任意两个单位之间的一条最短的路径. 2. 从图中任意单位地点出发的一条深度优先遍历路径. 主函数: 1 #include <ios

算法10 之带权图

上一节我们已经看到了图的边可以有方向,这一节里,我们将探讨边的另一个特性:权值.例如,如果带权图的顶点代表城市,边的权可能代表城市之间的距离,或者城市之间的路费,或者之间的车流量等等. 带权图归根究底还是图,上一节那些图的基本操作,例如广度优先搜索和深度优先搜索等都是一样的,在这一节里,我们主要来探讨一下带权图的最小生成树最短路径问题. 最小生成树问题 首先探讨下最小生成树问题,它与上一节所提到的最小生成树不同.上一节的最小生成树是个特例,即所有边的权值都一样.那么算法如何设计呢?建议用优先级队

邻接矩阵表示有向带权图

#include <stdio.h> #include <stdlib.h> #include <string.h> typedef char VertexType[5]; //存储顶点值 #define MaxSize 50 #define INIT 10000 typedef struct //邻接矩阵,存储弧的信息 {     int adj; }ArcNode,AdjMatrix[MaxSize][MaxSize]; typedef struct   //图的类

无向带权图的最小生成树算法——Prim及Kruskal算法思路

边赋以权值的图称为网或带权图,带权图的生成树也是带权的,生成树T各边的权值总和称为该树的权. 最小生成树(MST):权值最小的生成树. 生成树和最小生成树的应用:要连通n个城市需要n-1条边线路.可以把边上的权值解释为线路的造价.则最小生成树表示使其造价最小的生成树. 构造网的最小生成树必须解决下面两个问题: 1.尽可能选取权值小的边,但不能构成回路: 2.选取n-1条恰当的边以连通n个顶点: MST性质:假设G=(V,E)是一个连通网,U是顶点V的一个非空子集.若(u,v)是一条具有最小权值的

[算法] 带权图

最小生成树(Minimum Span Tree):对于带权无向连通图.所有节点都连通且总权值最小.应用:电缆布线.网络.电路设计 找V-1条边,连接V个顶点,总权值最小 切分定理(Cut Property):给定任意切分,横切边中权值最小的边必属于最小生成树 切分:把图中节点分为两部分 横切边:边的两个端点属于切分的不同两边 证明:反证法,假设横切边中一条权值不是最小的边属于最小生成树,给生成树添加横切边中权值最小的边形成环,删掉权值不是最小的边打破环得到新的权值更小的生成树,与假设矛盾 实现:

有向有权图的最短路径算法--Dijkstra算法

Dijkstra算法 1.定义概览 Dijkstra(迪杰斯特拉)算法是典型的单源最短路径算法,用于计算一个节点到其他所有节点的最短路径.主要特点是以起始点为中心向外层层扩展,直到扩展到终点为止.Dijkstra算法是很有代表性的最短路径算法, 在很多专业课程中都作为基本内容有详细的介绍,如数据结构,图论,运筹学等等.注意该算法要求图中不存在负权边. 问题描述:在无向图 G=(V,E) 中,假设每条边 E[i] 的长度为 w[i],找到由顶点 V0 到其余各点的最短路径.(单源最短路径) 2.算