1. 程式人生 > >回溯法演算法實驗

回溯法演算法實驗

     

班級  軟體工程16-1  學號  3162052051116  姓名  張識虔  同組實驗者              

實驗名稱        回溯法                       

日期 2018 11 14

 一、實驗目的與要求:

1、通過回溯法的示例程式理解回溯法的基本思想;

2、運用回溯法解決實際問題進一步加深對回溯法的理解和運用;

 

二、實驗內容:

1、分析並掌握“0—1揹包” 問題的回溯演算法求解方法;

       2、 程式設計實現回溯法的典型例題。

 

三、實驗步驟

1.理解回溯演算法思想和演算法示例;

2.上機輸入和除錯演算法示例程式;

3.理解實驗題的問題要求;

4.上機輸入和除錯自己所編的實驗題程式;

5.驗證並分析實驗題的實驗結果;

6.整理出實驗報告;

 

四、示例程式1 0-1揹包問題

0-1揹包:給定n種物品和一揹包。物品i的重量是wi,其價值為vi,揹包的容量為C。問應如何選擇裝入揹包的物品,使得在總重量不超過揹包的容量C的前提下裝入揹包中物品的總價值最大。

  原始碼見Knapsack.cpp, Knapsack_bound.cpp

 

五、實驗題

1. 教材210頁習題9第5題。(也就是子集和問題)

給定一個正整數集合X={x1,x2,….,xn}和一個正整數y,設計回溯演算法,求集合X的一個子集Y,

使得中元素之和等於y

#include<iostream>
using namespace std;

#define NUM 10000
int n;
int c;
int cw;
int bestw;
int w[NUM];
int x[NUM];
int r;
bool flag;

void backtrack(int t)
{
	if(t>n)
	{
		if(cw==c)
		{
			for(int i=1; i<=n; i++){ 
				if (x[i]){
			    cout<<w[i]<<"  ";
			}
			}
			flag = false;
			cout<<endl<<endl;
		}
		return;
	}
	r -= w[t];
	if (cw+w[t]<=c)
	{
		x[t] = 1;
		cw += w[t];
		backtrack(t+1);
		cw -= w[t];
	}
	if (cw+r>bestw)
	{
		x[t] = 0;
		backtrack(t+1);
	}
	r += w[t];
}

int main()
{
	cout<<"輸入n個數,輸入c被求和"<<"       輸入 0 0 結束程式"<<endl;
	while(scanf("%d%d",&n,&c) && (n||c))
	{
		r = 0;
		cout<<"輸入"<<n<<"個數字"<<endl; 
		for(int i=1; i<=n; i++)
		{
			cin>>w[i];  
			r += w[i];      
		}
		cw = 0;
		bestw = 0;
		flag = true;
		if(flag){
		cout<<endl<<"輸出組合方式"<<endl;
	}
		backtrack(1);
		if (flag) 
		cout<<"沒有答案"<<endl;
	}
	return 0;
}

 

 

2. 整數變換問題:整數i的兩種變換定義為,(向下取整);設計一個演算法求給定兩個整數a和b,用最少次數的和變換將整數a變換為b;例如

 

題目分析:

觀察f和g兩個函式發現,f總是使得自變數x變大,g總是使得自變數x變小。因此我們在決定讓x執行哪個函式之前必須先判斷x和目標值m之間的大小關係。如果x>m,就讓其執行g函式;反之,執行f函式。

這道題目有兩種情況,一種是有解,即n可以通過函式變換成m;另一種是無解,即n無法通過函式變換成m。第一種情況比較容易,即只需要判斷最後的x是否等於m即可。如果x等於m,那麼說明n已經被變換成m了,遞迴返回。第二種情況,如果在遞迴的過程,出現了前面計算過的元素,那就說明n是無法轉換成m的。

用回溯法解決整數變換問題,用子集樹。

 

剪枝條件:

顯示約束:如果x>m,就剪掉它的左子樹;如果x<m,就剪掉它的右子樹;

隱式約束:如果在某次計算的過程中發現當前的計算次數已經大於或等於最少計算次數了,就剪掉這個分支。

 

#include <iostream>
using namespace std;

#define max 10
int n,m;
int k;
bool goal;
int road[max+1];  //用於記錄g f 排列資訊 

int Fx(int t,int i)
{
	if(i==0)  //i用來標記 函式種類  
		return t/2;     //i=0   f(t)=t/2;
	else
		return 3*t;     //i=1   f(t)=3*t;
}

bool Search(int dep,int N)
{
	if(dep>k) return false;   //深度不能大於k 
	for(int i=0;i<2;i++)
	{
		int num=N;
		num=Fx(num,i);
		road[dep]=i;
		if(num==m||Search(dep+1,num))
		{
			goal=true;
			return true;
		}
	}
	return false;
}

void Procedure()
{
    k=1;
	goal=false;
	while(!Search(1,n))   //回溯 
	{
		k++;
		if(k>max)
			break;
		if(goal)
			break;

	}
}


int main()
{
	cout<<"\t輸入要求變換的數n:";
	cin>>n;
	cout<<endl;
	cout<<"\t輸入要求變換成什麼數m:";
	cin>>m;
	cout<<endl;
	for(int i=0;i<max;i++)
	{
		road[i]=0;
	}
	Procedure();
	if(goal)
	{
		cout<<"\t"<<n<<"  可以變換成 "<<m<< ",並且得到的最小變換次數為:";
		cout<<k<<endl<<endl;
		cout<<"\t得到的結果為     "<<m<<" = ";
		for(int i=1;i<=k;i++)
		{
			if(road[i]==0){
			cout<<" "<<"g";
			}	
			else if(road[i]==1){
				cout<<" "<<"f";
			}
		}
		cout<<"("<<n<<")";
	}
	else
		cout<<"\t沒有結果!"<<endl;
	return 0;
}