穷举搜索

对于一些困难的问题,我们只能穷举(brut force)搜索,是的,我们知道“穷举搜索”这个词,可是,具体怎么穷举呢?

很多问题可以归结到图的搜索,对于图的搜索,大家应该都知道 DFS 和 BFS,然而可能没有多少人知道如何对图做穷举搜索。我先举一个最简单的例子:哈米尔顿环(hamilton cycle),不知道的,可以去维基百科上去查。

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>

using namespace std;

int start = 0;
int N;
vector<vector<int> > G;
vector<int>  P; // current path/stack
vector<int>  D; // vertex depth in stack, -1 indicate not yet discovered
				// so D also serve as Mark array of DFS

void printham() {
	printf("found hamilton cycle: ");
	for (int i = 0; i < N; ++i)
		printf("%d ", P[i]);
	printf("\n");
}
void hamilton(int x, int k) {
	if (k == N) {
		assert(0); // never goes here
	} else {
		D[x] = k; // mark x
		P[k] = x; // put x into the path
		for (int j = 0, c = G[x].size(); j < c; ++j) {
			int y = G[x][j];  // Edge(x to y)
			if (D[y] != -1) { // found a cycle
				// (1 + k - D[y]) is the cycle length
				// so the condition is also (D[y] == 0 && k == N-1)
				if (y == start && k == N-1)
					printham(); // this is a hamilton cycle
			} else // not yet discovered
				hamilton(y, k+1);
		}
		D[x] = -1; // un-mark
	}
}

int main(int argc, char* argv[])
{
	int lineno = 1;
	while (!feof(stdin)) {
		int x, y;
		int fields = scanf("%d %d\n", &x, &y);
		if (fields == 2) {
			if (min(x, y) < 0) {
				fprintf(stderr, "invalid vertex number at line %d\n", lineno);
				return 3;
			}
			if (G.size() < (size_t)max(x, y) + 1)
				G.resize(max(x, y) + 1);
			G[x].push_back(y);
		} else {
			fprintf(stderr, "bad record at line %d\n", lineno);
			return 3;
		}
		lineno++;
	}
	N = G.size();
	P.resize(N);
	D.resize(N, -1);
	if (argc >= 2)
		start = atoi(argv[1]);
	else
		start = 0;
	hamilton(start, 0);
	return 0;
}
为了突出递归的逻辑,这个程序把所有(每次递归都相同的)函数参数换成了全局变量,从软件工程角度讲这是有问题的,但是作为示例,逻辑上更清楚一些。

这个程序从 stdin 读取一个图,这个图共 N 个结点,结点编号是 0~N-1。每行是一条边 src dest,中间用空白分隔。

从代码效率上讲,这个程序也也不够高效,一个在数据结构上(how to optimize vector<vector<int> > G ?)和程序逻辑(非递归) 上充分优化的版本,大约比这个要快10倍。

这个程序也接受一个参数 start ,表示搜索的起始结点,start  的默认值是 0。

下面是一个图的示例(g1.txt):

0 1
1 2
3 5
2 4
5 1
0 2
2 3
3 4
5 0
4 5

运行 hamilton 3 < g1.txt

输出结果是:found hamilton cycle: 3 4 5 0 1 2

穷举的一般代码框架是:

bruteforce(x, depth) {
    if (depth == max_depth) {
     // ....
    } else {
          mark[x] = true;
          for (y : x.children) {
              if (mark[x])
                     // ...
              else
                     bruteforce(y, depth+1);
           }
           mark[x] = false; // this is the only diff with DFS
      }
}

posted on 2011-07-17 15:58  能发波  阅读(302)  评论(0)    收藏  举报

导航