最近新学习了一种新的求解的方法,就是暴力搜索,在通常做题没有很明确的思路的时候,通常都会采用的一种方式。
我们知道,一个问题的解空间通常对应的是一棵树的方式进行组织的,那么我们可以通过根据题目中的条件描述来扫描
树中的每一个结点,对应的就是将问题的所有可能的解进行扫描一遍,从中选出满足要求的即为问题的答案了。
这也就是人们常说的搜索,搜索的对象是解空间树。 当然,根据不同的情况而言,树中的结点个数可能会有很多,
很多情况下,在规定的时间之内是不能够搜索到全部的解所对应的顶点的,所以这也就随之产生了很多中搜索的方法:
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 的全部全排列