1. 程式人生 > >分配問題-分支限界法

分配問題-分支限界法

// branchBound_assignJob.cpp : 定義控制檯應用程式的入口點。
//

#include "stdafx.h"

#include <iostream>
#include <cstring>
#include <vector>
using namespace std;

#define  N 4

//case 1 --> N=5
//int c[][N]={
//	3,  8,  4, 12,16,
//	9, 12, 13,  5,12,
//	8,  7,  9,  3,10,
//	12, 7,  6,  8,11,
//	23, 17, 26, 18, 13
//};

//case 2 --> N=4
int c[][N]={
	3,  8,  4, 12,
	9, 12, 13,  5,
	8,  7,  9,  3,
	12, 7,  6,  8
};


//case 3 --> N=3
//int c[][N]={
//	4, 3, 2,
//	2, 4, 6,
//	8, 7, 5
//};


int costBoundInK = 65535;//搜尋深度為k時的花費下界,深度為k有N-k個節點的下界,取最小值給minCostInk
int minCostInk = 65535;//搜尋深度為k時的最小下界
int minCostLine = 0;//記錄最小下界節點所在的行
int expanded[N];//存放被擴充套件節點的順序
vector<int> vectors;//存放搜尋深度為k時的花費下界,即costBoundInK

int min_exceptiInRow(int i,int row)
{
	int min = 65535;
	for (int line=0; line<N; line++)
	{
		bool currExpanded =false;
		for (int m=0;m<N;m++)
		{
			if (expanded[m] == line)//line行的某一節點已經之前被擴充套件過,故這一行的節點不能再擴充套件
			{
				currExpanded = true;
				break;
			}
		}
		//功能:除去當前行和之前被擴充套件的節點所在行之後,通過迴圈找出row這列的最小值
		if (line!=i  && !currExpanded && c[line][row]<min)//與i不在同一行,同時節點未被擴充套件,且是這一列中的最小值
		{
			min = c[line][row];
		}
	}
	return min;
}

/************************************************************************
**功能:求出最小的花費
**引數:k為作業編號
		mincost為最終返回的最小花費
************************************************************************/
void branchBound(int k,int& mincost)
{
	while(true)
	{
		if (k<N)//未到葉子節點
		{
			bool currExpanded = false;
			minCostInk = 65535;
			for (int i=0; i<N; i++)
			{
				for (int m=0;m<N;m++)
				{
					if (expanded[m]==i)//檢視當前行的某一節點是否已經被擴充套件過,若擴充套件過,則當前行的所有節點都不能再擴充套件
					{
						currExpanded = true;
						break;
					}
				}
				if (!currExpanded)//當前行未被擴充套件
				{
					costBoundInK = c[i][k];//costBoundInK表示在某個搜尋深度k下,把作業k分配給工人i時的時間下界
					for (int j=k+1;j<N;j++)
					{
						costBoundInK += min_exceptiInRow(i,j);//深度k下的花費下界,即在未擴充套件節點所在列均取花費的最小值,並累加
					}
					if (costBoundInK < minCostInk)
					{						
						minCostInk = costBoundInK;
						minCostLine = i;		//記錄要擴充套件節點的行號
						expanded[k] = i;		//expanded[k]記錄花費矩陣中節點被擴充套件的順序,k為列,代表作業編號,依次為0,1...N
												//對應的i為行,代表工人編號,即分配方案為工人i完成作業k
					}
					vectors.push_back(costBoundInK);//不同深度k下計算的花費下界存入vectors容器;深度為k時,需要計算N-k次下界,取最小值為擴充套件節點
				}
				currExpanded = false;
				expanded[k]=-1;//同級節點擴充套件,恢復當前節點為未擴充套件
			}
			expanded[k] = minCostLine;//選取當前搜尋深度k下,花費最少的為擴充套件節點c[minCostLine][k]
			k++;
		}
		else//到達葉節點
		{
			for(int i=0;i<N;i++)//根據擴充套件節點的順序,計算最小花費
			{
				mincost +=c[expanded[i]][i];
			}
			break;
		}
	}
}

int main(int argc, char *argv[])
{
	memset(expanded, -1, N*sizeof(int));//給陣列置-1

	int mincost=0;
	branchBound(0,mincost);//分支限界演算法,從根節點開始擴充套件,由mincost返回最少花費


	//輸出
	cout<<"-------花費矩陣(行為工人,列為作業,從0開始)----------"<<endl;
	for (int i=0;i<N;i++)
	{
		for (int j=0;j<N;j++)
		{
			printf("%3d ",c[i][j]);	
		}
		cout << endl;
	}
	cout<<"---------------------------------------------"<<endl;

	for(int i=0;i<N;i++)
	{
		cout<< "作業:" << i  << " --> 由工人" << expanded[i] << "完成. "<< " 花費為:" << c[expanded[i]][i] << " " << endl;
	}

	cout << "總花費為:"<< mincost<< endl;
	return 0;
}