1. 程式人生 > >布線問題(分支限界法)

布線問題(分支限界法)

fine 問題 let 導致 return [1] 使用 記錄 描述

一、首先說一下分支限界法的思想:

(1)比較:分支限界法和回朔法有相似之處,但是回朔法是搜索問題的所有解,采用深度優先搜索;而分支限界法是搜索問題的最優解,采用的是廣度優先搜索

(2)核心思想:分支限界法中,每一個活節點都只有一次機會成為擴展節點。活節點一旦成為擴展節點,就一次性產生所有的兒子節點。在這些兒子節點中,導致不可行解或者導致非最優解的兒子節點被舍棄,其余兒子節點被加入活節點表中。此後,從活節點表中取下一節點成為當前擴展節點,並重復上述節點的擴展過程。這個過程一直在持續到找到所需要的最優解或者活節點表為空時為止;其中:選擇擴展節點的方式可以分為:隊列式分支限界法 和 優先隊列式分支限界法。後者相對於前者的改進是對活節點加入了優先級,優先級最高的成為擴展節點(通常通過最大堆最小堆實現);

二、布線問題描述:

技術分享圖片

技術分享圖片

代碼如下:

//隊列類 : LinkedQueue.h
#ifndef LINKEDQUEUE_H
#define LINKEDQUEUE_H

template <class Type>
class LinkedQueue{

public:
	LinkedQueue(){};

	explicit LinkedQueue(int Capacity);  //創建隊列
	bool IsEmpty();  //判斷是否空
	bool IsFull();  //判斷是否滿
	bool Add(Type &cell);  //向隊列中加入元素
	bool Delete(Type &cell);  //刪除隊列中的元素

	~LinkedQueue();

private:
	Type cell;
	Type *ptr;  //隊列中的元素指針
	int QueueLen;  //隊列元素個數
	int QueueCapacity;  //隊列容量
	int Head;
	int Tail;

};


template <class Type>
LinkedQueue<Type>::~LinkedQueue()
{
	delete[]ptr;
	ptr = nullptr;
}

template <class Type>
LinkedQueue<Type>::LinkedQueue(int Capacity)
{
	QueueCapacity = Capacity;
	Head = 0;
	Tail = 0;
	QueueLen = 0;
	ptr = new Type[QueueCapacity];
}

template <class Type>
bool LinkedQueue<Type>::IsEmpty()
{
	if (QueueLen == 0)
		return true;
	else
		return false;
}

template <class Type>
bool LinkedQueue<Type>::IsFull()
{
	if (QueueLen == QueueCapacity)
		return true;
	else
		return false;
}

template <class Type>
bool LinkedQueue<Type>::Add(Type &cell)
{
	if (IsFull())
		return false;
	else
	{
		ptr[Tail] = cell;
		Tail++;
		QueueLen++;
		return true;
	}
}

template <class Type>
bool LinkedQueue<Type>::Delete(Type &cell)
{
	if (IsEmpty())
		return false;
	else
	{
		cell = ptr[Head];
		Head++;
		QueueLen--;
		return true;
	}
}

#endif
//使用分支限界法解決布線問題main.cpp
//====================================================
#include <iostream>
#include "LinkedQueue.h"
//#include <queue>  

using namespace std;

int n, m;  //布線盤是n * m大小

class Position{
	public:
		int row;
		int col;
};

bool FindPath(int ** grid , Position start, Position finish, int &PathLen, Position * &path)
{
	//計算從起始位置start到目標位置finish的最短布線路徑
	//找到最短布線路徑則返回true,否則返回flase
	if ((start.row == finish.row) && (start.col == finish.col))
	{
		PathLen = 0;
		return true;
	}
	//設置方格陣列“圍墻”
	for (int i = 0; i < m + 1; i++)
	{
		grid[0][i] = grid[n + 1][i] = 1;  //頂部和底部
	}
	for (int i = 0; i < n + 1; i++)
	{
		grid[i][0] = grid[i][m + 1] = 1;  //左翼和右翼
	}
	//初始化相對位移
	Position offset[4];
	offset[0].row = 0; offset[0].col = 1;  //右
 	offset[1].row = 1; offset[1].col = 0;  //下
	offset[2].row = 0; offset[2].col = -1;  //左
	offset[3].row = -1; offset[3].col = 0;  //上
	int neigh_num = 4;  //相鄰方格數
	Position here, nbr;
	here.row = start.row;
	here.col = start.col;
	grid[start.row][start.col] = 2;
	//標記所有可以到達的方格位置

	LinkedQueue<Position> Q(n * m + 1);  //隊列
	//queue<Position> Q();  //隊列

	//標記可到達的相鄰方格
	do {
		for (int i = 0; i < neigh_num; i++)
		{
			nbr.row = here.row + offset[i].row;
			nbr.col = here.col + offset[i].col;
			if (grid[nbr.row][nbr.col] == 0)  //該方格未被鎖定
			{
				grid[nbr.row][nbr.col] = grid[here.row][here.col] + 1;
				if ((nbr.row == finish.row) && (nbr.col == finish.col))  //完成布線
					break;
				Q.Add(nbr);  //壓入隊列稱為活節點
			}
		}
		//是否到達目標位置finish?
		if ((nbr.row == finish.row) && (nbr.col == finish.col))  //完成布線,是否要加這一步?
			break;
		//活節點隊列是否非空
		if (Q.IsEmpty())  //無解
			return false;
		Q.Delete(here);  //取下一個擴展節點
	} while (true);
	//構造最短布線路徑
	PathLen = grid[finish.row][finish.col] - 2;
	path = new Position[PathLen];
	//從目標位置finish開始向起始位置回溯
	here = finish;
	for (int j = PathLen - 1; j >= 0; j--)
	{
		path[j] = here;
		//找前驅位置
		for (int i = 0; i < neigh_num; i++)
		{
			nbr.row = here.row + offset[i].row;
			nbr.col = here.col + offset[i].col;
			if (grid[nbr.row][nbr.col] == j + 2)
				break;
		}
		here = nbr;  //向前移動
	}
	return true;
}

int main(void)
{
	Position start, finish;  //開始位置和目標位置
	int PathLen;  //最短路徑的長度
	Position *path;  //記錄的最短路徑
	cout << "請輸入布線盤的大小,n * m 規格: " << endl;
	cin >> n >> m;
	cout << "請輸入開始位置(x , y) :" << endl;
	cin >> start.col >> start.row;
	cout << "請輸入結束位置(x , y) :" << endl;
	cin >> finish.col >> finish.row;

	int ** grid = new int*[n + 2];
	for (int i = 0; i < n + 2; i++)
	{
		grid[i] = new int[m + 2];
	}
	for (int i = 0; i < n + 2; i++)
	{
		for (int j = 0; j < m + 2; j++)
		{
			grid[i][j] = 0;
		}
	}

	FindPath(grid, start, finish, PathLen, path);

	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j <= m; j++)
		{
			cout << grid[i][j] << "  ";
		}
		cout << endl;
	}

	cout << "最短路徑是: " << endl;
	cout << PathLen << endl;

	system("pause");
	return 0;
}

效果圖類似:

技術分享圖片

布線問題(分支限界法)