C++——《算法分析与设计》实验报告——单源最短路径问题

实验名称: 单源最短路径问题

实验地点:

实验目的:

1、  理解分支限界法的剪枝搜索策略;

2、  掌握分支限界法的算法柜架;

3、  掌握分支限界法的算法步骤;

4、  通过应用范例学习动态规划算法的设计技巧与策略;

 

实验原理

1. 基本思想

分支是使用广度优先策略,依次生成扩展结点的所有分支。

限界是在结点扩展过程中,计算结点的上界,搜索的同时剪掉某些分支。

分支限界法就是把问题的可行解展开,再由各个分支寻找最佳解。

与回溯法类似,分支限界法也是在解空间中搜索得到解;

不同的是,分支限界法会生成所有扩展结点,并舍弃不可能通向最优解的结点,然后根据广度优先/最小耗费优先,从活结点中选择一个作为扩展结点,使搜索向解空间上有最优解的分支推进。

2. 搜索策略

分支限界法首先生成当前扩展结点的所有分支,然后再从所有活结点中选择一个作为扩展结点。每一个活结点都要计算限界,根据限界情况判断是否剪枝,或选择最有利的结点。

实验内容:

1、使用分支限界法解决单源最短路径问题。

2、通过上机实验进行算法实现。

3、保存和打印出程序的运行结果,并结合程序进行分析,上交实验报告。

源程序:

方法一:

//单源最短路径

class Graph{	//带权有向图
private:
    int n;			//顶点个数
    int **c;		//邻接矩阵
    int *dist;		//记录路径
public:
    void shortestPaths(int);
    Graph();         //根据情况构造图
};

class MinHeapNode{		//最小堆的结点
    friend Graph;
private:
    int i;				//结点对应的顶点编号
    int length;			//结点记录的最短路径
public:
    int getI(){ return i; }
    void setI(int i){ this->i = i; }
    int getLength(){ return length; }
    void setLength(int length){ this->length = length; }
};

class MinHeap{		//最小堆(虽然叫堆,但其实并不是用堆实现的)
    friend Graph;
private:
    int length;			//最小堆的长度,等同于顶点个数
    MinHeapNode *nodes;		//结点数组
public:
    MinHeap(int n)
    {
        this->length = n;
        nodes = new MinHeapNode[n];
    }
    void deleteMin(MinHeapNode&);	//令当前节点出队列,并给出下一个扩展结点
    void insertNode(MinHeapNode N)		//结点入队列,将原结点的内容替换即可
    {
        nodes[N.getI()].setI(N.getI());
        nodes[N.getI()].setLength(N.getLength());
    }
    bool outOfBounds()		//检查队列为空
    {
        for(int i = 0;i < length;i++)
            if(nodes[i].getI() != -1)
                return false;
        return true;
    }
};

void MinHeap::deleteMin(MinHeapNode &E)
{
    int j = E.getI();
    nodes[j].setI(-1);
    nodes[j].setLength(-1);		//标记为出队列
    int tmp = INT_MAX;
    for(int i = 0;i < length;i++){		//给出路径最短的结点作为扩展结点
        if(nodes[i].getI() != -1 && nodes[i].getLength() < tmp){
            E.setI(i);
            E.setLength(nodes[i].getLength());
            tmp = nodes[i].getLength();
        }
    }
}

void Graph::shortestPaths(int start)
{
    MinHeap heap = MinHeap(n);	
    MinHeapNode E = MinHeapNode();		//别问,一开始还加了new,太久不写C++了
    E.i = start;	
    E.length = 0;
    dist[start] = 0;	//初始为源点V,对应编号start
    while(true){
        for(int j = 0;j < n;j++){	//检查所有邻接顶点
            if(c[E.i][j] != 0){		//是否邻接
                if(E.length + c[E.i][j] < dist[j]){		//是否满足限界,当前路径小于记录的最短路径
                    dist[j] = E.length + c[E.i][j];		//更新
                    if(/*填入判断表达式*/){          //没有邻接顶点的点不入队,按情况调整,没有也可以,但会造成无效开销
                        MinHeapNode N = MinHeapNode();	//创建一个新的结点,并令其入队列
                        N.i = j;
                        N.length = dist[j];
                        heap.insertNode(N);
                    }
                }
            }
        }
        if(heap.outOfBounds())	//队列为空,结束
            break;
        heap.deleteMin(E);	//该结点已经生成全部分支,出队列并取得下一扩展结点
    }
}

方法二:

#include <iostream>
#include<queue>
using namespace std;
 
typedef struct ArcCell{
    int adj;//保存权值
    int info;//存储最短路径长度
}ArcCell,AdjMaxtrix[100][100];
 
typedef struct{
    int data;
    int length;
}VerType;
 
typedef struct{
    VerType vexs[100];//顶点向量
    AdjMaxtrix arcs;
    int vexnum;//顶点数
    int arcnum;//弧数
}Graph;
 
Graph G;
queue<int> q;
 
void CreateGraph()
{
    int m,n,t;
    printf("输入顶点数和弧数:");
    scanf("%d%d",&G.vexnum,&G.arcnum);
    printf("输入顶点:");
    for(int i=1;i<=G.vexnum;i++)
    {
        scanf("%d",&G.vexs[i].data);
        G.vexs[i].length=10000;
    }
 
    for(int i=1;i<=G.vexnum;i++)
        for(int j=1;j<=G.vexnum;j++)
        {
            G.arcs[i][j].adj=0;
        }
 
    printf("输入弧及权重:\n");
    for(int i=1;i<=G.arcnum;i++)
        {
            scanf("%d%d%d",&m,&n,&t);
            G.arcs[m][n].adj=1;
            G.arcs[m][n].info=t;
        }
 
}
 
int NextAdj(int v,int w)
{
    for(int i=w+1;i<=G.vexnum;i++)
        if(G.arcs[v][i].adj)
            return i;
    return 0;//not found;
}
 
void ShortestPaths(int v)
{
    int k=0;//从首个节点开始访问
    int t;
    G.vexs[v].length=0;
    q.push(G.vexs[v].data);
    while(!q.empty())
    {
        t=q.front();
        k=NextAdj(t,k);
        while(k!=0)
        {
            if(G.vexs[t].length+G.arcs[t][k].info<=G.vexs[k].length)//减枝操作
            {
                G.vexs[k].length=G.vexs[t].length+G.arcs[t][k].info;
                q.push(G.vexs[k].data);
            }
            k=NextAdj(t,k);
        }
        q.pop();
    }
}
 
void Print()
{
    for(int i=1;i<=G.vexnum;i++)
        printf("%d------%d\n",G.vexs[i].data,G.vexs[i].length);
}
 
int main()
{
    CreateGraph();
    ShortestPaths(1);
    Print();
    return 0;
}

方法三:

#include <iostream>
#include<vector>
#include<queue>
using namespace std;
 
typedef struct ArcCell{
    int u;//保存权值
    int info;//存储最短路径长度
    ArcCell(int a,int b):u(a),info(b){
	}
}ArcCell,AdjMaxtrix[100][100];

typedef struct{
    int data;
    int length;
}VerType;
 
typedef struct{
    VerType vexs[100];//顶点向量
    vector<ArcCell>arcs[1001];
    int vexnum;//顶点数
    int arcnum;//弧数
}Graph;
 
Graph G;
queue<int> q;
 
void CreateGraph()
{
    int m,n,t;
    printf("输入顶点数和弧数:");
    scanf("%d%d",&G.vexnum,&G.arcnum);
    printf("输入顶点:");
    for(int i=1;i<=G.vexnum;i++)
    {
        scanf("%d",&G.vexs[i].data);
        G.vexs[i].length=10000;
    }
    printf("输入弧及权重:\n");
    for(int i=1;i<=G.arcnum;i++)
        {
            scanf("%d%d%d",&m,&n,&t);
            G.arcs[m].push_back({n,t});
        }
 
}
 
void ShortestPaths(int v)
{
    int k=0;//从首个节点开始访问
    int t;
    G.vexs[v].length=0;
    q.push(G.vexs[v].data);
    while(!q.empty())
    {
        t=q.front();
 		for(int i=0,j=G.arcs[t].size(); i<j; i++)
        {
        	
       	 	k=G.arcs[t][i].u; 
       	 	printf("%d------%d\n",t,k);
            if(G.vexs[t].length+G.arcs[t][i].info<=G.vexs[k].length)//减枝操作
            {
                G.vexs[k].length=G.vexs[t].length+G.arcs[t][i].info;
                q.push(G.vexs[k].data);
            }
           
        }
        q.pop();
    }
}
 
void Print()
{
    for(int i=1;i<=G.vexnum;i++)
        printf("%d------%d\n",G.vexs[i].data,G.vexs[i].length);
}
 
int main()
{
    CreateGraph();
    ShortestPaths(1);
    Print();
    return 0;
}

 

实验结果:

 

心得与体会:

算法分析:

生成根节点的所有分支,全部入队列并记录路径;

在队列中选择路径最短的分支作为扩展结点

逐个生成分支,并判断分支的路径是否小于记录的最短路径;

若不小于,舍弃该分支;

若小于,该分支入队列;

生成所有分支后,回到2;

当队列为空时,算法结束。

 

体会:

1、  理解分支限界法的剪枝搜索策略;

2、  掌握分支限界法的算法柜架;

3、  掌握分支限界法的算法步骤;

4、  通过应用范例学习动态规划算法的设计技巧与策略;

5、  使用分支限界法解决单源最短路径问题。

 

参考文章

https://blog.csdn.net/weixin_44712386/article/details/105532881

https://blog.csdn.net/gzj_1101/article/details/48955177

https://www.jianshu.com/p/372dc2571784

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章