寒假训练报告1.29(最短路)

先说句关于二分图的问题。今天看书发现二分图判定可以使用dfs,然而在做题时惊奇的发现,一道二分图的题,正是前几天做的并查集的题。因此,得出一个未必正确的结论:二分图判断既可以使用DFS,也可以使用并查集。

接下来系统地整理几种最短路的方法。
1.Floyd-Warshall
解决:全局最短路
不能使用情况:带有“负权环”的图
存储方式:邻接矩阵
时间复杂度:O(v^3)
空间复杂度:O(v^2)
算法思想:Floyd算法是一个经典的动态规划算法。从i到j只有直接到达和经过中间点k两种情况。
PS为什么Floyd的循环顺序是 k i j?
S:Floyd算法的本质是DP,而k是DP的阶段,因此要写最外面。
f[k][i][j]表示i和j之间可以通过编号为1…k的节点的最短路径。
初值f[0][i][j]为原图的邻接矩阵,f[k][i][j] = min(f[k-1][i][j] , f[k-1][i][k]+f[k-1][k][j])
f最外层一维空间可以省略,因为f[k]只与f[k-1]有关。

void floyd(){
    for(int k = 1; k <= n; k++) //遍历所有中间点
        for(int i = 1; i <= n; i++) //遍历所有起点
            for(int j = 1; j <= n; j++) //遍历所有终点
                if(f[i][j] > f[i][k] + f[k][j]) //不断更新i到j的最短路
                    f[i][j] = f[i][k] + f[k][j];
}

2.Dijkstra算法
解决:单源最短路
不能使用情况:图中含有负权边
最初时间复杂度:O(V*V+E)
源点可达:O(V*lgV+E*lgv) => O(E*lgV)
稀疏图:O(V^2)
用斐波那契堆实现优先队列:O(V*lgV+E)
算法思想:本质是贪心的思想。每次在剩余节点中找到离起点最近的节点放到队列中,并用来更新剩下的节点的距离,再将它标记上表示已经找到到它的最短路径,以后不用更新它了。

1 )Dijkstra+邻接矩阵 O(V^2)+路径输出

int e[maxn][maxn];  //邻接矩阵
int d[maxn];    //从起点到某一点的最短距离
bool v[maxn];   //标记是否在集合S中
int p[maxn];    //保存路径
int n;  //顶点数
void dijkstra(){
    fill(d, d + n, INF);
    fill(v, v + n, 0);
    fill(p, p + n, -1);
    d[s] = 0;
    while(1){
        int u = -1;
        for(int j = 0; j < n; j++)//寻找一个距离最近的没有使用过的点
             if(!v[j] && (u == -1 || d[j] < d[u]))   
                  u = j;
        if(u == -1) break;  //所有的点都被使用过了,就break
        v[u] = 1;   //将最近点u放入集合s中
        for(int j = 0; j < n; j++){ //在集合s中放入u后,对于每个点再次维护
            d[j] = min(d[j], d[u] + e[j][u]);
            p[j] = u;
        }
    }
}
//获取起始点到顶点t的最短路径
vector<int> Getpath(int t){
    vector<int> path;
    while(t != 1){
        path.push_back(t);
        t = p[t];
    }
    reverse(path.begin(), path.end());
    return path;
}

2 )Dijkstra+邻接表+堆优化

struct edge{
    int to, w;
}
vector<edge> G[maxn];
typedef pair<int, int> P;
int d[maxn];    //从起点到某一点的最短距离
int n;  //顶点数
void dijkstra(int s){
    priority_queue<P, vector<P>, greater<P> > q;
    fill(d, d + n, INF);
    d[s] = 0;
    q.push(P(s, 0));    //加源点入最小堆
    while(!q.empty()){
        P p = q.top;    //取出堆顶的点,也就是距离最小的点
        q.pop();
        int v = p.first;
        if(d[v] < p.second)  continue;  //加入队列前更新过,就不必再更新
        for(int i = 0; i < G[v].size(); i++){
            edge e = G[v][i];
            if(d[e.to] > d[v] + e.w){   //能更新其他点,被更新的点加入队列
                d[e.to] = d[v] + e.w;
                q.push(P(e.to, d[e.to]));
            }
        }
    }
}

3.Bellman-Ford算法
适用范围:带负权图单源最短路
缺点:效率低
时间复杂度:O(VE)
算法思想:连续进行松弛,在每次松弛时把每条边都更新一下,若在n-1次松弛后还能更新,则说明有负环。
优化:SPFA算法(队列优化)

int u[maxn], v[maxn], w[maxn];//从顶点u[i]到顶点v[i]这条边的权值为w[i]
for(int k = 1; k <= n - 1; k++)
    for(int i = 1; i <= m; i++)
        d[v[i]] = min(d[v[i]], d[u[i]] + w[i]);
//检测负权回路
bool flag = 0;
for(int i = 1; i <= m; i++)
    if(d[v[i]] > d[u[i]] + w[i])    flag = 1;
if(flag)    printf("含有负权回路\n");

4.SPFA算法
适用范围:单源最短路,可判负环
时间复杂度:O(kE)
缺点:效率不稳定,最好情况每个结点只入队一次,变为BFS,复杂度为O(E);最坏情况每个结点入队(V-1)次,退化为Bellman-ford算法,复杂度为O(VE).
原理:在Bellman-ford基础上加上队列优化
1)SPFA+手动实现队列

void spfa(int s){   //求单源点s到其他顶点的最短距离
    fill(d, d + n, INF);
    d[s] = 0;
    v[s] = 1;
    q[1] = s;
    int u, head = 0, tail = 1;
    while(head < tail){ //队列非空
        head++;
        u = q[head];    //取队首元素
        v[u] = 0;   //释放队首元素,可能还被用来松弛其他结点
        for(int i = 0; i <= n; i++)
        if(e[u][i] > 0 && d[i] > d[u] + e[u][i]){
            d[i] = d[u] + e[u][i];
            if(!v[i]){
                tail++;
                q[tail] = i;
                v[i] = 1;
            }
        }
    }
}

2)SPFA+邻接矩阵+BFS
//特点:判负环不稳定

bool spfa_bfs(int s){
    queue<int> q;
    fill(d, d + n, INF);
    fill(t, t + n, 0);
    fill(v, v + n, 0);
    d[s] = 0;
    q.push(s);
    v[s] = 1;
    t[s] = 1;
    while(!q.empty()){
        int u = q.front();
        q.pop();
        v[u] = 0;
        for(int i = 0; i <= n; i++){
            if(d[i] > d[u] + e[u][i]){
                d[i] = d[u] + e[u][i];
                if(!v[i]){
                    q.push(i);
                    v[i] = 1;
                    t[i]++;
                    if(t[i] > n)
                        return false;
                }
            }
        }
    }
    return true;
}

3)SPFA+邻接矩阵+DFS
//DFS判负环很快

bool spfa_dfs(int u){
    v[u] = 1;
    for(int i = 0; i <= n; i++){
        if(d[i] > d[u] + e[u][i]){
            d[i] = d[u] + e[u][i];
            if(v[i])    return 1;
            else if(spfa_dfs(i))    return 1;
        }
    }
    v[u] = 0;
    return 0;
}

例题:
POJ 3255
次短路问题
到某个顶点v的次短路要么是到其他某个顶点u的最短路再加上u->v的边,要么是到u的次短路加上u->v的边,因此求到所有顶点的最短路和次短路。用Dijkstra+heap,用dis1[]和dis2[]保存最短路和次短路即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值