用分支定界算法求以下問題:
某公司於乙城市的銷售點急需一批成品, 該公司成品生產基地在甲城市。甲城市與乙城市之間共有 n 座城市,互相以公路連通。甲城市、乙城市以及其它各城市之間的公路連通情況及每段公路的長度由矩陣M1 給出。每段公路均由地方政府收取不同額度的養路費等費用, 具體數額由矩陣 M2 給出。請給出在需付養路費總額不超過 1500 的情況下,該公司貨車運送其
產品從甲城市到乙城市的最短運送路線。
具體數據參見文件:
M1.txt: 各城市之間的公路連通情況及每段公路的長度矩陣(有向圖); 甲城市爲城市 Num.1,乙城市爲城市 Num.50。(9999表示不連通)
M2.txt: 每段公路收取的費用矩陣(非對稱)。
解題思路:
利用Length_bound[i]和Cost_bound[i]記錄搜索過程中到該城市路線總長度和總花費都最小的
界,再搜索到該城市時,如果其長度和花費均大於bound,則剪枝,如果長度和花費均小於bound,
則更新bound(該bound主要爲了避免陷入自環)
利用Dijkstra算法分別找出從終點城市出發到所有其他城市的最短路徑長度Min_Length[i]以
及最小花費Min_Cost[i],並以此也作爲bound。
再從起點城市出發,利用DFS進行路徑搜索,每前進一個城市,判斷已走路徑長度與當前城市
到終點的最短路徑之和Sum_Length + Min_Length[i]是否大於已經搜索到的當前最優路線的路徑
長度Best_Length,若大於則無需向下搜索,否則再比較已走路徑花費與前城市到終點的最小花費
之和Sum_Cost + Min_Cost[i]是否大於1500,若大於則無需向下搜索。
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <fstream>
#include <sstream>
#include <vector>
#include <queue>
#include <time.h>
#define City_Num 50 //城市的數量
#define N_MAX (City_Num + 1) //定義鄰接矩陣的大小爲城市數量+1
#define INF (0x3f3f3f3f)
using namespace std;
struct Port {
int ID; //節點的序號
int Sum_Length; //從起點出發至該節點走過的路程
Port(int id, int Length) {
ID = id; Sum_Length = Length;
}
bool operator <(const Port& s)const {
return Sum_Length > s.Sum_Length; //按照走過的路徑長度排序,最小堆
}
};
struct Edge {
int to;
int length;
int price;
Edge(int too, int lengthh, int pricee) {
to = too; length = lengthh; price = pricee;
}
};
//vector<Edge>City[N_MAX]; //定義鄰接鏈表
int Road[N_MAX][N_MAX], Value[N_MAX][N_MAX]; //定義鄰接矩陣
int Min_Length[N_MAX], Min_Cost[N_MAX]; //定義由終點出發到到其他城市的最短路徑長度以及最小花費
int Length_bound[N_MAX], Cost_bound[N_MAX]; //長度和花費bound
bool Flag[N_MAX]; //1表示已經搜索過該城市
int Result[N_MAX], Search[N_MAX]; //輸出的最短路線,當前搜索的路線
int Best_Length = 9999; //定義最短可行路徑總長度
void Dijkstra_Len(int Start);
void Dijkstra_Cost(int Start);
/*
num:爲搜索到的第幾層
Sum_Length:已走路徑總長
Sum_Cost:已走路徑花費
*/
int K = 0;
void DFS(int num, int Sum_Length, int Sum_Cost)
{
int front = Search[num];
if (front == City_Num)
{
if (Sum_Length < Best_Length) //比最好的好,更新
{
for (int i = 1; i <= num; i++)
{
Result[i] = Search[i];
//cout << Result[i] << ' ';
}
Best_Length = Sum_Length;
//cout << endl << "len = " << Best_Length << " cost = " << Sum_Cost << endl;
}
return;
}
for (int i = 1; i < N_MAX; i++) //鬆弛操作
{
if (Road[front][i] != 9999)
{
//cout <<"asf = " << i << endl;
int SUM_L = Sum_Length + Road[front][i]; //路徑總長
int SUM_C = Sum_Cost + Value[front][i]; //總花費
if (SUM_L + Min_Length[i] >= Best_Length) //第一個bound
//if (SUM_L >= Best_Length)
continue;
if (SUM_C + Min_Cost[i] > 1500) //第二個bound
//if (SUM_C > 1500)
continue;
//第三個bound,主要避免在環內循環搜索
if (SUM_L >= Length_bound[i] && //長度和花費均大於bound,剪枝
SUM_C >= Cost_bound[i])
{
continue;
}
else if (SUM_L < Length_bound[i] && //長度和花費均小於bound,更新bound
SUM_C < Cost_bound[i])
{
Length_bound[i] = SUM_L;
Cost_bound[i] = SUM_C;
}
Search[num + 1] = i;
K++;
DFS(num + 1, SUM_L, SUM_C);
}
}
}
int main()
{
int N, M;
clock_t start, stop; //定義clock_t類型的start和end
ifstream road("m1.txt");
ifstream value("m2.txt");
for (int i = 1; i < N_MAX; ++i)
{
for (int j = 1; j < N_MAX; ++j)
{
road >> N;
value >> M;
Road[i][j] = N;
Value[i][j] = M;
//if (N != 9999) //9999即可視爲不連通
//{
// Edge E(j,N,M);
// City[i].push_back(E);
//}
}
}
start = clock();
Dijkstra_Len(City_Num);
Dijkstra_Cost(City_Num);
memset(Length_bound, INF, sizeof(Length_bound));
memset(Cost_bound, INF, sizeof(Cost_bound));
Search[1] = 1;
DFS(1, 0, 0);
stop = clock();
cout << "算法運行時間 = " << (double)(stop - start) / CLOCKS_PER_SEC << endl;
cout << "搜索次數 = " << K << endl;
cout << "最小路徑總長度 = " << Best_Length << endl << "最佳路線: ";
for (int i = 1; Result[i] != City_Num && i<= N_MAX; ++i)
{
cout << Result[i] << ' ' ;
}
cout << 50 << endl;
return 0;
}
//找出從終點城市出發到所有其他城市的最短路徑長度Min_Length[i]
void Dijkstra_Len(int Start)
{
memset(Min_Length, INF, sizeof(Min_Length));
Min_Length[Start] = 0;
memset(Flag, 0, sizeof(Flag));
priority_queue<Port>Save;
Save.push(Port(Start, Min_Length[Start]));
while (!Save.empty())
{
Port Front = Save.top(); //上一個到達的點
Save.pop();
if (Flag[Front.ID]) //該城市已經搜索過
continue;
Flag[Front.ID] = 1;
/*
for (int i = 0; i < City[Front.ID].size(); i++) //鬆弛操作
{
Edge E = City[Front.ID][i];
if (!Flag[E.to])
{
if (Min_Length[E.to] > Min_Length[Front.ID] + E.length)
{
Min_Length[E.to] = Min_Length[Front.ID] + E.length;
Save.push(Port(E.to, Min_Length[E.to]));
}
}
}
*/ //鄰接鏈表
for (int i = 1; i < N_MAX; i++) //鬆弛操作
{
if (Road[i][Front.ID] != 9999)
{
//cout << City[Front.ID][i].length << ' '<< City[Front.ID][i].price << endl;
if (!Flag[i])
{
if (Min_Length[i] > Min_Length[Front.ID] + Road[i][Front.ID])
{
Min_Length[i] = Min_Length[Front.ID] + Road[i][Front.ID];
Save.push(Port(i, Min_Length[i]));
}
}
}
}
}
}
//找出從終點城市出發到所有其他城市的最小花費Min_Cost[i]
void Dijkstra_Cost(int Start)
{
memset(Min_Cost, INF, sizeof(Min_Cost));
Min_Cost[Start] = 0;
memset(Flag, 0, sizeof(Flag));
priority_queue<Port>Save;
Save.push(Port(Start, Min_Cost[Start]));
while (!Save.empty())
{
Port Front = Save.top(); //上一個到達的點
Save.pop();
if (Flag[Front.ID]) //該城市已經搜索過
continue;
Flag[Front.ID] = 1;
for (int i = 1; i < N_MAX; i++) //鬆弛操作
{
if (Road[i][Front.ID] != 9999)
{
//cout << City[Front.ID][i].length << ' '<< City[Front.ID][i].price << endl;
if (!Flag[i])
{
if (Min_Cost[i] > Min_Cost[Front.ID] + Value[i][Front.ID])
{
Min_Cost[i] = Min_Cost[Front.ID] + Value[i][Front.ID];
Save.push(Port(i, Min_Cost[i]));
}
}
}
}
}
}