1. 程式人生 > >暴力搜尋---新技能get

暴力搜尋---新技能get

   最近新學習了一種新的求解的方法,就是暴力搜尋,在通常做題沒有很明確的思路的時候,通常都會採用的一種方式。

我們知道,一個問題的解空間通常對應的是一棵樹的方式進行組織的,那麼我們可以通過根據題目中的條件描述來掃描

樹中的每一個結點,對應的就是將問題的所有可能的解進行掃描一遍,從中選出滿足要求的即為問題的答案了。

  這也就是人們常說的搜尋,搜尋的物件是解空間樹。 當然,根據不同的情況而言,樹中的結點個數可能會有很多,

很多情況下,在規定的時間之內是不能夠搜尋到全部的解所對應的頂點的,所以這也就隨之產生了很多中搜索的方法:

1.暴力搜尋法: 暴力搜尋就是以列舉的方法一一例舉解空間樹上的每一個結點,直至找到滿足要求的節點

主要有兩種型別題: 1. N個位置上的資料選取x 個,x 為任意數字,對應的N 個數據每個資料,兩種狀態  0  , 1 ,0代表沒有被選取, 1 代表的是選取

這樣的話, N 個數據,對應的可能有  2^N 中可能性,當然解空間樹全部遍歷,對應 2^N 個頂點,當然對應的時間複雜度為 O(2^n)

2. N個位置上的資料全部選取,但是選取的順序不同造成的結果不同------>對應的是對 N 進行全排列的方式,

這種實現方式有兩種,一種是暴力搜尋對應的時間複雜度為 O(N^N). 另一種是 O(N!) ,基於的是 dfs 搜尋的方式。

2.二分搜尋法 :通過每次將所訪問的解空間樹中的結點個數減半,即如果當前正在訪問的是解空間樹中的 i 頂點,

根據題目中所描述的資訊,選取 i 頂點的左子樹或是右子樹,然後丟棄沒有選擇的子樹。對選取的子樹進行同樣的操作。

不過這種搜尋方法要求的是,所搜尋的關鍵字是需要有序排列的,這樣每次在進行二分的時候,

才能夠保證所訪問的樹的頂點 i 的左右子樹的數量都是相同的,並且(左子樹結點值)< i < (右子樹結點值) ,或是另一種序列。

3. DFS 搜尋: 沿著一個起始訪問點,一直走直到訪問到底為止,深度優先搜尋。 

通常實現起來可以通過遞迴的方式來對其進行實現。 也可以使用模擬棧的方式來實現。

4. BFS搜尋: 逐層遍歷搜尋樹中的每一個結點,BFS通常是用來選取最優解的方式,因為逐層遍歷的方式,如果發現滿足題意的解的話,

該解必定位於的是所有的可能解中的距離根節點最近的一個結點,所以是最優解。

通常實現的方式是基於 佇列 作為輔助資料結構來實現。

-------------------------------------------------------------------------------------------------------------------------------------------------------

1.暴力搜尋法程式設計小練習:

    1.1 已知,一個揹包能夠存放最大物體的重量為 C , 現在有 N 個物體,對應的重量是 w[ 0 ... N-1], 請你求出,將物體中選取出一些 (x 個, x <= n ),

使得這 x 個物體的總共重量不超過揹包能夠承受的最大的重量C, 求出這個最大的重量。

輸入資料格式

N C

w[0] ....w[N-1]

輸出資料的格式

放入包中的物品最大重量值

</pre><pre name="code" class="cpp">#include <cstdio>
#include <string.h>
#include <algorithm>


int ans = 0 ;
int N , C , w[1005] ;
 


void input( )
{


	memset( w , 0 , sizeof ( w ) ) ;
	
	scanf("%d%d", &N , &C ) ;
	
	for ( int i = 0 ; i < N ; i++ )
	{
		scanf("%d", &w[i]) ;
	}
}


void dfs ( int step , int  nowW )
{
	 if ( step == N )
	 {
		if ( nowW <= C )
		{
			ans = ans>nowW?ans:nowW ;
		 
		}
		
		return ;
	 }
	 
	 
	 dfs( step+1 , nowW) ;
	 
	 
	 dfs( step+1 , nowW+w[step] ) ;
 
}


int main ( void )
{
	input() ;
	
	dfs( 0 , 0 ) ;
	
	if ( ans > 0  )
	printf("ans = %d\n", ans ) ;


	else 
		printf("No") ;
	
	return 0 ;
}


   1.2 已知,一個揹包能夠存放最重的重量為 C , 現在有 N 個物體,N 個物體所對應的重量為 w[ 0.. N-1] , 請你求出,在不對每個物品進行分割

並且不超過揹包限定總重量的情況下,能夠存放的最多物品的個數是多少,此時揹包的重量是多少,存放物品的編號是多少( 0...N-1)

輸入資料格式

N C

w[0] w[1] ... w[ N-1 ]

輸出資料的格式

最多的物品個數

揹包重量

所存放的物品編重量 ( 0 .. N-1 )

#include <cstdio>
#include <algorithm>

using namespace std ;

int N , C ;
int w[1001] ;

void input( )
{
	scanf("%d%d", &N , &C) ;
	
	for ( int i = 0 ; i < N ; i++ )
	{
	scanf("%d", &w[i]) ;
	}
}

int cnt = 0 ;
int sum = 0 ;

void  fun()
{
	sort ( w , w+N ) ;
	
	for ( int  i = 0 ; i < N ; i++ )
	{
		if ( sum + w[i] <= C )
		{
			sum += w[i] ;
			cnt++;
		}
		else
		break;
	}
}

int main ( void )
{
	input() ;
	fun() ;
	
	if ( cnt != 0 )
	{
	printf("%d\n", cnt ) ;
	printf("%d\n", sum) ;
	for ( int i = 0 ; i < cnt ; i++ )
	   printf("%d  ", w[i]) ;
	}
	else
		printf("No") ;
	return 0 ;
}


 1.3 一直一個數組 a[ 0..n-1] 中有 n 個數值,現在給你一個固定的數值 K, 試問,在陣列 a 中是否存在一組數值使得這些數值的和剛好等於 K

如果存在的話,輸出 Yes。 如果不存在的話,則輸出 No 

輸入格式

n K

a[0] a[1] ... a[n-1]

輸出格式

Yes

No

#include <cstdio>
#include <string.h>
#include <iostream>


using namespace std ;

int a[101] ,n , k ;
bool ok = false ;
 

void input ()
{
	scanf("%d%d", &n , &k ) ;
	
	 
	for ( int i = 0 ; i < n ; i++ )
		cin>>a[i] ;
}
void dfs ( int step , int v )
{
		if ( step == n )
		{
			if ( v == k ) ok = true ;
			return ;
		}
		
		 
		dfs(  step+1, v+a[step] ) ;

		 
		
		dfs(step+1 , v ) ;
}


int main ( void )
{
	input() ;
	
	dfs ( 0 , 0 ) ;
	
	if ( ok )
	{
		printf("Yes") ;
	}
	
	else
	printf("No") ;
	return 0 ;
}

在這裡突然發現一個問題,dfs 應該在不對其進行剪枝操作的前提下是暴力搜尋的一種特例。

//1.4 給定一個數值N, 打印出 N 到 1 的全部全排列