分支限界解最短路径

  • 问题描述:
  • 要求:
    • 随机数算法
    • 分支限界算法
  • 程序源码

问题描述:

给定一个m行n列的矩阵,从左上角开始每次只能向右或向下移动,最后到达右下角的位置,路径上的所有数字累加起来作为这条路径的路径和。编写程序求所有路径和中的最小路径和。例如,一下矩阵中的路径1->3->1->0->6->1->0是所有路径中路径和最小的,结果为12.

1 3 5 9

8 1 3 4

5 0 6 1

8 8 4 0

要求:

1)使用随机数算法生成一个行数和列数均大于10000的矩阵,其中随机数范围为[0,100],相邻数字差距大于10,每个数字出现频率相近(考察报告中部分截图显示即可);

2)基于搜索算法(蛮力法、回溯法、分支限界法)设计一个程序解决该问题;

随机数算法

使用rand和srand生成随机数,由于rand() 函数产生的随机数是伪随机数,是根据一个数值按照某个公式推算出来的,这个数值称之为“种子”。种子和随机数之间的关系是一种正态分布,通过 srand() 函数来重新“播种”,这样种子就会发生改变,使用时间作为参数,只要每次播种的时间不同,那么生成的种子就不同,最终的随机数也就不同。使用rand() % (b - a + 1) + a生成[a,b]的随机数,传入0和100可生成[0,100]的随机数,使用last存放上一次的随机数的结果,即矩阵前一个数的结果,使用now存放当前随机数,使用while循环判断直到前一个和当前值大于10,给now赋予新值并添加到矩阵中。这样就可以使矩阵相邻的数差距大于10.

分支限界算法

首先想无论是使用蛮力法还是其他的方法都是先将题目做出来,由于输入是一个矩阵,使用数组存放,假设有M行N列,路径要从左上角走到右下角,而且每次只能向右走或者向下走,仔细观察发现无论用哪种方法向下走或向右走的步数永远分别是M,N减一,一共要走的步数就是M+N-2。那么可以想象他的解空间树,他是一个二叉树,只有向右走或者向下走,假设左子树是向下走,右子树是向右走,解空间树的高度就是M+N-2。解空间树中每个结点存放当前所在行与当前所在列,当前结点的总路径长度和解向量,用FIFO表示队列(实际上对应层次遍历),初始时,FIFO=[ ],每遍历一层将其入队列。

采用STL的queue容器qu作为队列,队列中的结点类型声明如下:

现在设计限界函数,为了简便,设根结点为第0层,然后各层依次递增,当结点所在行数和列数相加等于M+N-2时表示是叶子结点层。解空间树如下

由于该问题是求路径和最小,属求最小值问题,采用下界设计方式,假设整体的下界为smallest,初始的smallest设置的初始路径是矩阵最上面一行和最右边一列。对于第i层的某个结点e,用(e.row,e.column)表示结点e的位置,用e.length表示走到当前结点的路径和,那么当前结点的路径和为:

e1.length = e.length + Arr[e.column][e.row + 1],那么此时价值的下界为e1.length,即后面的结点都不选可达到的路径和,如果此时的下界超过了smallest,则进行减枝。如果路径向右或者向下走的步数超过了行数或者列数,同样进行减枝。

路径向下走的剪枝

路径向右走进行剪枝

核心代码,使用广度优先搜索求最短路径:


结点进队并判断是否为叶子结点,如果是叶子结点,判断此时的路径是否为最短路径,如果比最短路径还要短则将此时的路径赋值给最短路径,并将当前结点的解向量赋值给最优解向量,用于输出。

实验结果与分析:

测试数据由(1)随机生成的,矩阵大小为18*18,其中随机数范围为[0,100],相邻数字差距大于10,每个数字出现频率相近,一共运行三分钟,此程序的时间复杂度为logM+N。

本机配置,CPU:i7-8750,内存:16G,当程序运行时候打开任务管理器发现,内存几乎跑满,但是CPU只有一小部分的消耗,说明此程序调用内存,消耗内存更多,但是运算更少,现在对老师说过的递归调用函数消耗内存更大有了更深刻的理解。

再次测试10*10的矩阵观察输出,当输入矩阵,输出路径和最短路径。

程序源码

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <time.h>
#include <queue>
#define M 18
#define N 18
int Arr[M][N] = {{0}};
int x[M + N - 1] = {0}; //最优解向量
int smallest = 0;       //最短路径和using namespace std;//队列结点类型
struct NodeType //队列结点类型
{int row;                    //所在行号int column;                 //所在列号int length;                 //当前结点总路径长度int thisx[M + N - 1] = {0}; //当前结点包含的解向量
};//生成随机数
void random(int a, int b)
{int now = 0, last = 0;srand((int)time(0)); //for (int i = 0; i < M; i++){for (int j = 0; j < N; j++){while (-11 < last - now && last - now < 11){now = rand() % (b - a + 1) + a;}Arr[i][j] = now;last = now;}}
}void EnQueue(NodeType e, queue<NodeType> &qu)
//结点e进队qu
{if (e.row + e.column == M + N - 2) //到达叶子结点{if (e.length < smallest) //找到路径最短的解{smallest = e.length;for (int j = 0; j < M + N - 1; j++)x[j] = e.thisx[j];}}elsequ.push(e); //非叶子结点进队
}void bfs()
{//给最短路径设置初始值,假设他一直向右走再一直向下走for (int i = 0; i < M; i++){smallest += Arr[0][i];}for (int i = 1; i < N; i++){smallest += Arr[i][M - 1];}NodeType e, e1, e2;queue<NodeType> qu;e.row = 0;e.column = 0; //建立源点结点e(根结点)e.length = Arr[e.row][e.column];e.thisx[0] = e.length;qu.push(e);         //源点结点e进队while (!qu.empty()) //队列不空循环{e = qu.front();//cout<<e.column<<" "<<e.row<<" "<<e.length<<endl;qu.pop();                                                            //出队列结点eif (e.length + Arr[e.column][e.row + 1] < smallest && e.row < M - 1) //向下走,检查下界并进行剪枝{e1.row = e.row + 1;e1.column = e.column;e1.length = e.length + Arr[e1.column][e1.row];for (int i = 0; i < e1.row + e1.column; i++) //复制解向量{e1.thisx[i] = e.thisx[i];}e1.thisx[e1.row + e1.column] = Arr[e1.column][e1.row];EnQueue(e1, qu); //向右走的结点进队操作}if (e.length + Arr[e.column + 1][e.row] < smallest && e.column < N - 1) //向右走,检查下界并进行剪枝{e2.row = e.row;e2.column = e.column + 1;e2.length = e.length + Arr[e2.column][e2.row];for (int i = 0; i < e2.row + e2.column; i++)e2.thisx[i] = e.thisx[i];e2.thisx[e2.row + e2.column] = Arr[e2.column][e2.row];EnQueue(e2, qu); //向右走的结点进队操作}}
}int main()
{random(0, 100);for (int i = 0; i < M; i++){for (int j = 0; j < N; j++)cout << Arr[i][j] << " ";cout << endl;}clock_t start, finish; //clock_t为CPU时钟计时单元数start = clock();//clock()函数返回此时CPU时钟计时单元数bfs();cout << "解向量为";for (int i = 0; i < M + N - 1; i++){cout << x[i] << "->";}cout << "最短路径和为" << smallest << endl;finish = clock();                                                     //clock()函数返回此时CPU时钟计时单元数cout << "共耗时:" << double(finish - start) / CLOCKS_PER_SEC << endl; //finish与start的差值即为程序运行花费的CPU时钟单元数量,再除每秒CPU有多少个时钟单元,即为程序耗时return 0;
}

[分支限界]给定一个矩阵m*n,从左上角开始每次只能向右或者向下走,最后到右下角的位置共有多少种路径相关推荐

  1. 给定一个矩阵m,从左上角开始每次只能向右走或者向下走,最后达到右下角的位置

    /*  * 给定一个矩阵m,从左上角开始每次只能向右走或者向下走,最后达到右下角的位置,  * 路径中所有数字累加起来就是路径和,返回所有路径的最小路径和,如果给定的m如下,那么路径1,3,1,0,6 ...

  2. 给定一个矩阵m*n,从左上角开始每次只能向右或者向下走,最后到右下角的位置共有多少种路径

    题目描述 给定一个矩阵m*n,从左上角开始每次只能向右或者向下走,最后到右下角的位置,路径上所有的数字累加起来就是路径和,返回所有的路径中最小的路径和. 思路: 1.排列组合 要从A到B,必须向左走6 ...

  3. 给定一个矩阵m*n,从左上角开始每次只能向右和向下走,最后到右下角的位置共有多少种路径。

    #include <bits/stdc++.h> using namespace std; /*    每次只能向右或向下走    dp[m][n] = dp[m-1][n]+dp[m][ ...

  4. [算法]给定一个矩阵m*n,从左上角开始每次只能向右或者向下走,最后到右下角的位置共有多少种路径

    很经典的一道题 等同于:https://leetcode-cn.com/problems/unique-paths/ 在完美世界面试中遇到了. 每次都只能向右或者向下走,求出所有种情况. 当时想到的思 ...

  5. 请编写一个函数,计算n*m的棋盘格子(n为横向的格子数,m为竖向的格子数)沿着各自边缘线从左上角走到右下角,总共有多少种走法,要求不能走回头路,即:只能往右和向下走,不能往左和往上走。

    请编写一个函数,计算n*m的棋盘格子(n为横向的格子数,m为竖向的格子数)沿着各自边缘线从左上角走到右下角,总共有多少种走法,要求不能走回头路,即:只能往右和向下走,不能往左和往上走. 递归实现: # ...

  6. M×N的矩阵,从左上角走,只能向右或者向下走,要求走过的每个元素的值加起来的和最大,步数不限

    题目描述 M×N的矩阵,从左上角走,只能向右或者向下走,要求走过的每个元素的值加起来的和最大,步数不限 代码实现 import numpy as np def max_way(a,m,n):dp=[[ ...

  7. 一个m * n的网格,从最左上角出发,每次只能向右或者向下移动一格,问有多少种不同的方法可以到达最右下角的格子

    格子路径 题目: 在一个 2×2 的栅格中,从左上角出来,只能向右或向下移动,总共有 6 条路径可以到达栅格的右下角: 求m*n的网格中,有多少条移动路径? 以一个 20×20 的栅格为例,它有137 ...

  8. 给定一个m*n的格子或棋盘,问从左上角走到右下角的走法总数(每次只能向右或向下移动一个方格边长的距离。

    比如一个2*3的矩阵, 1 2 3 4 5 6 从1出发走到6,则可能的走法为:1 2 3 6, 1 2 5 6, 1 4 5 6共有三种. 这道题可以看成是深度优先遍历一颗树.解法为: public ...

  9. Leetcode:62题 不同路径(一个机器人位于一个 m x n 网格的左上角 。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角)

    题目: 一个机器人位于一个 m x n 网格的左上角 (起始点在下图中标记为"Start" ). 机器人每次只能向下或者向右移动一步.机器人试图达到网格的右下角(在下图中标记为&q ...

最新文章

  1. 什么是类别不平衡?有哪些解决方案?
  2. Shell-06 函数
  3. 【运维】详解 /etc/fstab
  4. Gstreamer中一些gst-launch常用命令
  5. asp中chr()函数及转义符对照表
  6. DataTime转Varchar
  7. 图像去重,4 行代码就能实现,你值得拥有imagededup
  8. AttributeError: module 'pymysql' has no attribute 'escape' 错误的出现以及解决
  9. 请问delphix下双缓冲是自动开启的吗?
  10. mysql8 修改密码_Mysql 8新特性之(1):账户与安全更人性化
  11. spring mvc实例
  12. 架构,改善程序复用性的设计~第二讲 什么应该提取出来,什么应该保留
  13. 又一国产数据库诞生!腾讯发布 TGDB,实时查询比 Neo4j 快 20-150 倍!
  14. 简单步骤Centos7安装Tomcat 8 (yum安装)(ZT)
  15. 定时 监控 shell 服务宕机自动重启,并发送短信通知
  16. 从月薪3000到月薪过万:做什么工作才能过上想要的生活
  17. 针对三层别墅的两种无线组网方案
  18. Call to a member function validate() on null
  19. 2020移动apn接入点哪个快_为什么别人的4g网总比你快? 手机这个设置没开启, 难怪网络差...
  20. 深度学习实战——CNN+LSTM+Attention预测股票

热门文章

  1. 域名解析文件异常怎么破?
  2. 区块链+战“疫”企业专访特辑之爱接力科技|链塔智库
  3. 2021 WIDC信息安全挑战赛预赛回顾与OBD-II标准阅读
  4. 用at命令编写简单的计划任务/cron计划任务
  5. 图像预处理——图像分割
  6. android开发时华为手机底部导航栏挡住了应用布局
  7. SQL文件更改默认打开方式
  8. batterystats文件解析
  9. UML基础(附绘制教程)
  10. Promise中的finally