暴力搜尋---新技能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 的全部全排列