1. 程式人生 > >經典剪枝演算法的例題——Sticks詳細註釋版

經典剪枝演算法的例題——Sticks詳細註釋版

這題聽說是道十分經典的剪枝算的題目,不要問我剪枝是什麼,我也不知道鄙視,反正我只知道用到了深度搜索

我參考了好多資料才悟懂,然後我發現網上的那些大神原理講的很明白,但程式碼沒多少註釋,看的很懵X,於是我抄起VS寫了個詳細註釋版,真的很詳細,史上最詳細,全宇宙最詳細,就這麼自信,不信你看,看不懂你咬我。

/*--------------------------------------------
*     剪枝演算法經典例題Sticks詳細註釋版
*---------------------------------------------*/

#include <iostream>

using namespace std;

//某根木棍被使用過就設定標誌位為1,沒使用過則設定標誌位為0
#define USED 1
#define UNUSED 0
#define MAXSIZE 64

//定義木棍結構體
typedef struct Stick
{
	int length;//每根木棍的長度
	int mark;//標誌位,記錄是否被使用過
}Sticks[MAXSIZE];

//雖然程式中使用全域性變數是不好的習慣,但做題who care
int n;//未拼接時的總木棒數
int g;//拼接好後的木棒數
int len;//滿足要求的拼接後的木棒的長度
Sticks sticks;//定義一個木棒集合,記錄未拼接時各木棒的資訊

//定義一個氣泡排序演算法,從大到小排序
void BubbleSort(Sticks *a,int num)
{
	int i,j;
	int temp;//交換時用的中間變數
	for (i = 0;i<num-1;i++)
	{
		for (j = 0;j<num-1;j++)
		{
			if ((*a)[j].length<(*a)[j+1].length)
			{
				//交換
				temp = (*a)[j].length;
				(*a)[j].length = (*a)[j+1].length;
				(*a)[j+1].length = temp;
			}
		}
	}
}

/*剪枝函式,深度搜索
*總共三個引數,nowLen表示現在拼接成的木棒的長度,
*nowGet表示現在拼接成的木棒的總數,此值若等於之前的g則說明找到符合要求的木棒長,
*cnt表示拼接過程中查詢剩下符合要求的木棒從哪個下標開始查詢,當cnt大於n時說明沒有符合要求的拼接方法
*該函式返回1代表找到了,0代表沒有找到符合要求的*/
int DFS(int nowLen,int nowGet,int cnt)
{
	if (cnt>=n) return 0;//找的下標都超了,肯定不滿足
	if (nowGet == g) return 1;//如果這個長度下獲取的總個數與g相等說明符合條件
	int i;

	//開始遍歷查詢
	for (i = cnt;i<n;i++)
	{
		if (sticks[i].mark==UNUSED)
		{
			//當找到的一組木棒恰好能拼接成需要的長度時
			if (nowLen+sticks[i].length == len)
			{
				sticks[i].mark = USED;//設定這個木棒已使用過
				//這組滿足,開始下一組尋找
				if (DFS(0,nowGet+1,nowGet)==1)
				{
					//遞迴,直到最後每一組都滿足需要的長度時說明這個長度可行
					return 1;
				}
				sticks[i].mark = UNUSED;//解除使用
				return 0;
			}
			//當找到的一組木棒還小於拼接成需要的長度時
			else if(nowLen+sticks[i].length < len)
			{
				sticks[i].mark = USED;//設定這個木棒已使用過
				if(DFS(nowLen+sticks[i].length,nowGet,i+1)==1)
				{
					//同樣遞迴,這裡說明一下i+1,這個意思是從i+1下標開始尋找要使這組木棒滿足要求的木棒
					return 1;
				}
				sticks[i].mark = UNUSED;//解除使用
				//下面這句表示如果當前搜尋時,前邊的長度為0,而第一根沒有成功的使用,
				//說明第一根始終要被廢棄,所以這種組合必定不會成功
				//此處的剪枝必須有,因為這裡的剪枝會節省很多的無用搜索,
				//缺少這一句超時
				if (nowLen == 0) return 0;
				//下面這句是指如果有一根木棒加上去已經知道不滿足要求了,則與它相同長度的木棒都可以跳過
				for ( ;sticks[i].length==sticks[i+1].length&&i+1<n;i++);
			}
		}
	
	}

	return 0;
}

int main()
{
	int i;
	int sum;//木棒的總長

	//使用者輸入每組的木棒數
	while(cin>>n,n)
	{
		//木棒一開始都初始化為未使用過的
		for (i = 0;i<MAXSIZE;i++)
		{
			sticks[i].mark = UNUSED;
		}
		sum = 0;
		for (i = 0;i<n;i++)
		{
			cin>>sticks[i].length;
			sum+=sticks[i].length;
		}
		
		//從大到小排序
		BubbleSort(&sticks,n);
		for (len = sticks[0].length;len<=sum;len++)
		{
			if (sum%len!=0) continue;//最後選的木棒長度一定是能被總木棒長整除的

			g = sum/len;//拼接的後的木棒數
			//剪枝,滿足要求退出迴圈
			if(DFS(0,0,0))
			{
				break;
			}
		}
		//輸出滿足要求的木棒長
		cout << len<<endl;
	}

	return 0;
}