两天来学习搜索算法的小结
算法中需要使用搜索进行解决的内容很多,大致分为以下几类常用的搜索方式。
1、枚举。枚举运算量很大,需要预先确定枚举的定义域。
2、广度优先搜索(BFS )——通常可以用于计算图的连通性、单源最短路径、计算最小操作次数等。
BFS的占用的是队列的空间,DFS 占用的是栈的空间(因为递归)。BFS和DFS的空间复杂度恰好相反。对链状图,BFS最好(队列中最多只有1个元素),DFS最差(所有节点都在根节点的递归内)。对起点与其他所有点相邻的图,DFS最好(递归深度为1),BFS最差(队列中放满了所有与起点相邻的图)。
相对来说,BFS是比较简单的(恩,可能我做的水题)。
反正大致的模板就是
queue<type> q;
q.push(初始状态);
while (!q.empty())
{
type t = q.front() ;
q.pop();
遍历 t 的各个Next状态 next
{
if (next is legal)
q.push(next); 计数或维护等;
}
}
但是BFS的状态数一多,需要的空间就会较大。因此就需要状态压缩,BUPT OJ上的1180就是一个典型的例子,但是状态压缩以及解压的时候,又会涉及效率,反正1180将80M的状态压缩到40K以后就超时了……
今天参考别人的代码,写了第一个DFS,POJ 1321。 相当的水。但是大牛的代码比我的简洁太多了。
DFS大致的模板就是
DFS(顶点)
{
处理当前顶点,记录为已访问
遍历与当前顶点相邻的所有未访问顶点
{
标记更改;
DFS( 下一子状态);
恢复更改;
}
}
回溯法:DFS适用于 显式图,但是对于一些隐式关系,我们需要使用回溯法,通过定义或找到各个状态、边界条件、搜索范围、约束条件和最优解结果进行建模求解。
边界条件:达到某状态时,需要检查并确定是继续搜索还是回到上一状态的条件(例如当前已使用时间比当前最优解要长,此时就不需要再进行搜索)
搜索范围:当前从当前状态开始进行搜索的所有下一级状态。
搜索范围:
另外一定要注意,假如参与递归的参数不是通过传参形式的方式进入递归的话,那么一定要做好数据恢复。
Trace(当前状态)
{
if 当前状态是结束状态
{
if 是最佳解: 记录。
退出
}
遍历当前状态的各个邻接状态
{
if 当前状态满足约束条件 且 满足最优性要求 : Trace(子状态)
}
}
回溯法经典题:任何大于1的自然数n,都可以拆分成若干个小于n的自然数之和,输入n,输出不同n的拆分方案。
状态:对于一个拆分a1<=a2<=...a(k-1)<=ak, 下一拆分可以是 a1<=a2<=...a(k-1)<=m<=ak-m (其中a(k-1)<=m<=ak-m)
#include <string.h>
#include <stdlib.h>
int a[20];
int result = 0;
int num;
void Sum(int kx) //传入参数为拆分结果的最后一项的位置
{
result = result + 1;
printf("%d\t%d = ",result,num);
for ( int i = 1 ; i <= kx-1 ; i++)
printf("%d +",a[i]);
printf("%d\n",a[kx]);
int k = kx;
int l = a[k];
for (int m = a[k-1]; m <= l/2; m++)
{
a[k] = m;
a[k+1 ] = l-m;
Sum(k+1);
}
}
int main()
{
while(1)
{
result = 0;
scanf("%d", &num);
for (int i = 1 ; i < num; i++)
{
memset(a,0,sizeof(a));
a[1] = i;
a[2] = num-i;
Sum(2);
}
}
return 0;
}
还有一个经典的题目是24点。
2
3 #include <iostream>
4 #include <stdio.h>
5 using namespace std;
6
7 double a[4];
8 bool b[4];
9 double ans;
10
11 bool judge(int step)
12 {
13 if (step == 3)
14 {
15 if ( a[3] - ans < 1e-8 && a[3] - ans > -(1e-8))
16 return true;
17 else
18 return false;
19 }
20
21 //else
22 for ( int j = 0 ; j < 3; j++)
23 for (int i = j+1; i < 4; i++)
24 {
25 double temp;
26 if ( !b[i] && !b[j])
27 {
28 temp = a[i];
29 b[j] = true;
30
31 a[i] = a[i] + a[j];
32 if (judge(step + 1)) return true;
33 a[i] = temp;
34
35 a[i] = a[i] - a[j];
36 if (judge(step + 1)) return true;
37 a[i] = temp;
38
39 a[i] = a[j] - a[i];
40 if (judge(step + 1)) return true;
41 a[i] = temp;
42
43 a[i] = a[i] * a[j];
44 if (judge(step + 1)) return true;
45 a[i] = temp;
46
47 if ( a[j] != 0)
48 {
49 a[i] = a[i] / a[j];
50 if (judge(step + 1)) return true;
51 a[i] = temp;
52 }
53
54 if (a[i] != 0)
55 {
56 a[i] = a[j] / a[i];
57 if (judge(step + 1)) return true;
58 a[i] = temp;
59 }
60 a[i] = temp;
61 b[j] = false; //回溯的时候要注意恢复现场
62 }
63 }
64
65 return false;
66 }
67
68 int main()
69 {
70 while (scanf("%lf%lf%lf%lf%lf", &a[0],&a[1],&a[2],&a[3],&ans) != EOF)
71 {
72 for ( int i = 0 ; i < 4; i++)
73 b[i] = false;
74
75 if (judge(0))
76 cout << "Possible" << endl;
77 else
78 cout << "Impossible" << endl;
79 }
80 }
拓扑排序应该也能归到这里吧,通常都是使用优先队列这个imba的数据结构。
#include <stdio.h>
#include <vector>
#include <queue>
#include <string.h>
using namespace std;
const int N = 100010;
//priority queue compare
struct less_comp
{
bool operator()(const int &a, const int &b)
{
return a > b;
}
};
vector<int> vec[N];
int n,k,indegree[N];
priority_queue< int, vector<int>, less_comp > q;
int main()
{
int n,k;
while(scanf("%d%d",&n,&k) && !(n==0 && k==0))
{
memset(indegree,0,sizeof(indegree));
int first,last;
for (int i = 0 ; i < k ; i++)
{
scanf("%d%d",&first,&last);
vec[first].push_back(last);
indegree[last]++;
}
// get 0 degree
for (int i = 1 ; i <= n ; i++)
{
if ( indegree[i] == 0)
q.push(i);
}
vector<int>::iterator it;
vector<int>::iterator itend;
int cur;
printf("ORDER:");
while(!q.empty())
{
cur = q.top();
q.pop();
itend = vec[cur].end();
for (it = vec[cur].begin(); it != itend; it++)
{
if (--indegree[*it] == 0)
{
q.push(*it);
}
}
vec[cur].clear();
printf(" %d",cur);
}
printf("\n");
}
}