北航研究生算法分析課的做業之一算法
用分支定界算法求如下問題:
某公司於乙城市的銷售點急需一批成品,該公司成品生產基地在甲城
市。甲城市與乙城市之間共有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; }