分支定界法

北航研究生算法分析课的做业之一算法

用分支定界算法求如下问题:
某公司于乙城市的销售点急需一批成品,该公司成品生产基地在甲城
市。甲城市与乙城市之间共有n 座城市,互相以公路连通。甲城市、乙城市以及其它各城市之间的公路连通状况及每段公路的长度由矩阵M1 给出。每段公路均由地方政府收取不一样额度的养路费等费用,具体数额由矩阵M2 给出。请给出在需付养路费总额不超过1500 的状况下,该公司货车运送其产品从甲城市到乙城市的最短运送路线。
具体数据参见文件:
M1.txt: 各城市之间的公路连通状况及每段公路的长度矩阵(有向图); 甲城市为城市Num.1,乙城市为城市Num.50。M2.txt: 每段公路收取的费用矩阵(非对称)数组

 

过程分析:数据结构

分支定界法最重要的一部就是定出合理的更紧的界,能够快速剪枝,本题先用floyd算法,讲distance和cost矩阵求出全部城市之间的最短距离矩阵graph_1和最短花费矩阵graph _2函数

用深度优先搜索路径,得出的一条从0号到49号城市的完整路径的长度,即为定界法的上届,试验其余路线,在试验其余路线时若是当前距离curdist+graph[cur][49]已经超出了现存的界,则此条路径无心义,直接剪枝,或者当前花费curcost+graph_2[cur][49](到达终点所可能的最短花费)超过1500,也剪枝。spa

当路径终点为49号城市时,即找到了一条可行的路线,而后更新mindist,distbound。当找不到能到达49号城市的路径时就回溯。指针

 

程序流程以下get

 

1.     将m1.txt,m2.txt的数据读入graph_1 and graph_2string

2.     用floyid算法求出全部点对之间的最短路长,和最小费用.产品

3.     声明并初始化一些变量和数据结构it

4.     创建一个堆栈,初始化该堆栈

5.     取出栈顶的结点,检查它的相邻结点(从上次考虑的那个结点的下一个结点开始考虑).肯定下一个当前最优路径上的结点.被扩展的结点都被加入堆栈中.

6.    在检查的过程当中,若是发现超出当前的路长界或超出费用的界,则进行”剪枝” ,而后回溯

7.     找到一个解后,保存便可,无需回溯,程序源文件中有详细说明缘由

8.      重复上一步的过程,直到堆栈为空.当前保存的解即为最优解.

 

代码

 

// zy1606522 孙涛 

 


#include<stdio.h>
#include<fstream>
#include<string>
#define MAX 10000000
#define N 50


void floyd(int n, int d[][N])//弗洛伊德算法 
{
    int i, j, k;
    
    for (k = 0; k < n; ++k)
    {
        for (i = 0; i < n; ++i)
        {
            for (j = 0; j < n; ++j)
            {
                if (d[i][k] + d[k][j] < d[i][j])
                {
                    d[i][j] = d[i][k] + d[k][j];
                
                }
            }
        }
    }
}
void matixcopy(int a[N][N], int b[N][N])       // 把矩阵b赋值给矩阵a 的函数 
{
    int i, j;
    for (i = 0; i < N; i++)
        for (j = 0; j < N; j++)
            a[i][j] = b[i][j];
}


int main()
{
    int stack[N];  int topstack;//栈顶指针 
     int visited[N];//visited函数记录当前已被访问城市 
    
    int i = 0, j = 0, k = 0;//定义循环变量 

    int graph_1[N][N], graph_2[N][N]; 
    FILE*fp;
    fp = fopen("m1.txt", "r");//读取m1 m2矩阵 ,注意m1.txt和m2.txt必须和cpp文件在同一个路径,不然出错
    if (fp == NULL)
    {
    
        return -1;
    }
    for (i = 0; i < N; i++)
        for (j = 0; j < N; j++)
            fscanf(fp, "%d", &graph_1[i][j]);
    fclose(fp);                     

    fp = fopen("m2.txt", "r");         
    if (fp == NULL)
    {
        
        return -1;
    }
    for (i = 0; i < N; i++)
        for (j = 0; j < N; j++)
            fscanf(fp, "%d", &graph_2[i][j]);
    fclose(fp);
    int m1[N][N], m2[N][N];
    matixcopy(m1, graph_1);        /*copy graph_1和graph_2 由于graph_1,graph_2将被弗洛伊德算法函数操做 */
    matixcopy(m2, graph_2);
    

    floyd(N, graph_1);
    floyd(N, graph_2);
    int curdist = 0, curcost = 0,distbound=MAX,costbound=MAX;//分别为当前距离,当前花费,距离界限,花费界限 

    topstack = 0;
    stack[topstack] = 0;
    stack[topstack + 1] = 0;
    visited[0] = 1;//visited数组记录当前被访问过的城市,避免再次访问 
    int bestpath[N];
    int  mindist=MAX, mincost=MAX;
    while (topstack>=0) /*当所有指针出栈结束 ,整个while循环的第一遍会搜索五十层,找出第一条可行路径,此后的循环被第一次生成的界限限制,会愈来愈短,因此最后一次生成的就是结果*/ 
    {
        int cur ,next;
    
        cur = stack[topstack];
        next = stack[topstack + 1]; //next很是重要,每次搜索从next+1开始,有效避免重复搜索,形成死循环 
        for (i = next+ 1; i < 50; i++)//必须从next+1开始搜索不然回溯将形成死循环 
        {
            if (m1[cur][i] == 9999)
                continue;
            if (visited[i] == 1)
                continue;
            if ((curdist + graph_1[cur][49] > distbound)||(curcost + graph_2[cur][49]>1500))//定界条件 
            {
                continue;//不知足的直接剪枝 
            }
            if (i < 50)
                break;
        }
        if (i == 50)//最后一个城市不是49,回溯 
        {
            topstack--;
            curdist -= m1[stack[topstack]][ stack[topstack + 1]];
            curcost -= m2[stack[topstack]][ stack[topstack + 1]];
            visited[stack[topstack + 1]] = 0;
        }
        else//记录可选的城市 
        {
            curdist += m1[stack[topstack]][i];
            curcost += m2[stack[topstack]][i];
            visited[i] = 1;
            topstack++;
            stack[topstack] = i;
            stack[topstack + 1] = 0;//下一个城市置为0,下一层搜索依然从1号城市开始 
            if (i == 49)//最后一个城市是49 
            {
                for(j=0;j<N;j++)//每次所得路径的城市个数不定,因此每次存储路径前要清洗数组 
                bestpath[j]=0;
                
                for (j = 0; j <= topstack; j++)
                    bestpath[j] = stack[j];//存储路径 
                if(curcost<1500)//上面的定界不能保证每一个curcost必定小于1500,最后一步可能超过,因此加限定条件 
                {
                     mindist = curdist;
                     mincost = curcost;
                     
                }
                
                if(curdist<distbound)
                distbound = curdist;   //换成更紧的界,剪枝更快 
                /*此处无需增长回溯代码,由于搜索到49以后,下次不管如何都不能找到49,程序必定会执行第100行,第100行的程序会向上回溯一次,但下次搜索从i=next+1=stack[topstack+1]+1号城市开始,所以不会通过49号城市,所以不会死循环。在没法找到能达到49的路径后,继续回溯,直到全部元素出栈,再也不知足while循环条件为止*/ 
            }
        }

    }
    printf("最短路程  %d\n最少花费 %d \n", mindist, mincost);

    printf("依次通过城市序号\n "); 
    for(int k=0;k<N;k++)
    {    
          
       printf("%d  ",bestpath[k]+1);
         if(bestpath[k]+1==N)//输出路径,到城市号为50中止 
         break;
    }
   printf("\n");
   getchar();

    return 0; }

相关文章
相关标签/搜索