您的位置:首页 > 其它

dfs

2015-12-08 16:14 375 查看

问题一:

问题描述: 输入一个数n,输出1~n的全部排列。

分析

我们可以将这个问题换一个思路来描述,假设有123编号的扑克牌和123编号的箱子,需要将扑克放到箱子里去,一共有多少种放的方法呢?

第一个箱子有3种,第二个剩下2种,最后一个1种,如此得到一种排列;那么,取出最后一个盒子的牌,退到倒数第二个箱子,此时我们有2种选择,去掉刚才已经选择过的,如此又得到了第二种排列;再退到倒数第三个箱子。。。这个过程有明显的探底性质,对吧。

如何将扑克放到箱子里面呢?

for (int i = 1; i <= n; i++) {
box[step] = i;    // step 表示我们正在第step箱子这里
}


而我们手上的扑克有的已经被选择了,有的还没有,因此,用一个book数组来标识是否还有这样的扑克

for (int i = 1; i <= n; i++)
{
if (book[i] == 0) {
box[step] = i;
book[i] = 1;
}
}


而处理完第step次之后,就应该处理第step+1次的箱子位置了吧?我们可以用递归的思想来处理吧?

void dfs(int step)
{
for (int i = 1; i <= n; i++)
{
if (book[i] == 0) {
book[i] = 1;
box[step] = i;     //本次选择第i个扑克,放到step箱子中

dfs(step + 1);     //递归,到第step+1次
book[i] = 0;       //dfs完成上一次选择之后,要退回上一次的选择
}
}
}


那么算法运行到什么时候终止本次选择呢?

也就是说我们应该为递归算法确定一个终止条件!

容易想到,箱子这里应该是step运行到n+1的时候,也就是已经排列好了1~n箱子的扑克!

对上述稍做修改:

void dfs(int step)
{
if (step == n + 1) {
// 打印1~n的排列
for (int i = 1; i <= n; i ++) {
print box[i];
}
return ;    //返回此次选择 !
}

for (int i = 1; i <= n; i++)
{
if (book[i] == 0) {
book[i] = 1;
box[step] = i;     //本次选择第i个扑克,放到step箱子中

dfs(step + 1);     //递归,到第step+1次
book[i] = 0;       //dfs完成上一次选择之后,要退回上一次的选择
}
}
}


实现:

思路是正确的,写出来:

#include <stdio.h>
#include <stdlib.h>

#define MAX 50

int n;
int box[MAX];
int book[MAX];

void dfs(int step)
{
if (step == n + 1) {
// one arrange
for (int i = 1; i <= n; i++)
printf("%d ", box[i]);
printf("\n");

return ;
}

for (int i = 1; i <= n; i++) {
if (book[i] == 0)  {
book[i] = 1;
box[step] = i;
dfs(step + 1);  // dfs
book[i] = 0;    //reuse
}

}
}

int main()
{
scanf("%d", &n);
dfs(1);

getchar();
return 0;
}




理解dfs的关键,在于知道当前该做什么。

之后再进入下一步。

可以总结下dfs的基本模型:

void dfs(int step)
{
//判断边界
//尝试每一种可能 for
{
//继续下一步
dfs(step + 1)
}
//返回
}


问题二:

利用上述思想,解一个相似的问题。

现在有1~9的数字,需要找出满足下列条件的数字组合,如何求解?

ABC + DEF = GHI

分析:

把数字当成盒子,然后用dfs做尝试,到达底部(step = 9 到达最后一个盒子)之后,输出满足条件的等式即可。

代码:

#include <iostream>
#include <cstdlib>

#define MAX 20

int box[MAX];   //number in the box
int number[MAX];    //number whether use or not?

static int count = 0;

bool meet()
{
if (100 * box[1] + 10 * box[2] + box[3] + 100 * box[4] + 10 * box[5] + box[6] ==
100 * box[7] + 10 * box[8] + box[9])
return true;
else
return false;
}

void dfs(const int step)
{
if (step == 10) {
if (meet() == true) {
std::cout<<box[1]<<box[2]<<box[3]<<" + "<<box[4]<<box[5]<<box[6]<<" = "
<<box[7]<<box[8]<<box[9]<<"   count = "<<++count<<std::endl;
}
return ;
}
for (int i = 1; i <= 9; i++) {
if (number[i] == 0) {
box[step] = i;
number[i] = 1;      //use this number

dfs(step + 1);      //recursive

number[i] = 0;      //这里一定要将刚才选择的取消,才能够进行下一个循环进行下一步尝试
}
}
}

int main()
{
std::cout<<"meet number : "<<std::endl;

dfs(1);     //begin

getchar();
return 0;
}


问题三:

有一个 n * m 规格的迷宫(<= 50),单元格要么是空地(0),要么是障碍物(1)

目的:找到一条从起点到终点的最短路径。

输入:

先输入迷宫的大小 n * m

接着输入迷宫,0 代表空地,1代表障碍

最后输入起点和终点的坐标

输出最短路径的距离。

example:

5 4

0 0 1 0

0 0 0 0

0 0 1 0

0 1 0 0

0 0 0 1

1 1 4 3

输出:

7

分析 :

用一个二维数组存迷宫,开始的时候我们在起点(1, 1),需要找到到终点(p, q)的最短路径。

在每个位置处,我们有四个方向可以走:上下左右。约定行走的顺序可以是:右下左上。

用dfs来分析,在当前位置(x, y)处,往可以走的方向进行探索,不能走的时候返回;

直到找到目的地为止(终止条件),然后更新我们的最短距离。

这里为了方便,我们定义一个方向数组:

int next[4][2] = {
{0, 1},     //right
{1, 0},     //down
{0, -1},    //left
{-1, 0}     //up
};


通过它来计算下一步的坐标很方便:

for (k = 0; k < 4; k++)
{
tx = x + next[k][0];
ty = y + next[k][1];
}


接下来就要对(tx, ty)进行判断了;

包括:

1. 是否越界

2. 是否是障碍物

3. 这个点是否已经在路径之中?(用一个标识数组book[][] 来记录是否已经走过它)

如果这个点符合上述所有要求,那么就可以进一步扩展它。

dfs(tx, ty, step + 1);

实现:

for (k = 0; k < 4; k++)
{
tx = x + next[k][0];
ty = y + next[k][1];

//判断是否越界
if (tx<1 || tx>n || ty<1 || ty>m)
continue;

//判断该点是否为障碍物或者已经在路径之中
if (a[tx][ty]==0 && book[tx][ty]==0) {
book[tx][ty] = 1;     //标记这个点已经走过

dfs(tx, ty, step + 1);

book[tx][ty] = 0;     //尝试结束,取消这个点的标记
}
}


来看看源码实现:

#include <stdio.h>
#include <stdlib.h>

#define MAX 50

int book[MAX][MAX]; //tag

int A[MAX + 1][MAX + 1]; // graph

/*
* order :
*
* right down left up
*
*/
int next[4][2] = { {0, 1}, //right {1, 0}, //down {0, -1}, //left {-1, 0} //up };

//matrix
int N, M;

// target
int final_x;
int final_y;

// count
static int min = 999;

void dfs(int x, int y, int step)
{
if (x == final_x && y == final_y) {
//printf("reach..\n");

if (step < min)
min = step;
return ;
}

for (int k = 0; k < 4; k++) {
int tx = x + next[k][0];
int ty = y + next[k][1];

if (tx > N || tx == 0 || ty > M || ty == 0)
continue;

if (book[tx][ty] == 0 && A[tx][ty] == 0) {
book[tx][ty] = 1;

dfs(tx, ty, step + 1);

book[tx][ty] = 0;
}
}

}

int main()
{
scanf("%d %d", &N, &M);
if (N <= 0 || M <= 0 || N > MAX || M > MAX) {
printf("N or M is out of range!\n");
return -1;
}

for (int i = 1; i <= N; i++)
for (int j = 1; j <= M; j++) {
scanf("%d", &A[i][j]);
}

int start_x, start_y;

scanf("%d %d %d %d", &start_x, &start_y, &final_x, &final_y);
if (final_x <=0 || final_x > MAX || final_y <= 0 || final_y > MAX) {
printf("final_x or final_y is out of range.");
return -1;
}
if (start_x <= 0 || start_x > MAX || start_y <= 0 || start_y > MAX) {
printf("start_x or start_y is out of range.") ;
return -1;
}

book[start_x][start_y] = 1; //already use the start point.

dfs(start_x, start_y, 0);

printf("result of min is : %d\n", min);

return 0;
}


问题四:

来看一个地图问题



我们有城市地图的数据,现在需要从1号点行走到5号点,如何去的路程最短呢?

数据是这样的:

5 8
1 2 2
1 5 10
2 3 3
2 5 7
3 1 4
3 4 4
4 5 5
5 3 3


第一行的输入表示5个城市,8代表边的条数

接下来的数据a b c 分别代表城市编号以及城市之间的距离数。

现在要求出起点(1)到终点(5)的最短路径?

分析:

可以用dfs也可以用bfs来实现。

先考虑dfs:

因为图中存在环路,有向图(无向图也一样),我们用dfs进行遍历的时候,可以用一个变量 min 来记录当前路径的代价,再用一个标识数组 (比如book)来标识当前路径上已经选择的节点,这样就能正确处理图中存在环的情况。

实现一:

#include <stdio.h>
#include <stdlib.h>

#define MAX 50

void DFS(int start);
void DFS_VISIT(int u, int sum);

int graph[MAX + 1][MAX + 1];    //graph
int node_record[MAX + 1];       //node identify

int stack_record[MAX + 1];
int top = 1;

int N, M;       // N * M

int min = 999999;

int start_node;     //start
int target_node;    //end

void DFS(int start)
{
node_record[start] = 1;

stack_record[top] = start;
top++;

for (int i = 1; i <= N; i++) {
if (start == i)
continue;
if (node_record[i] == 1)
continue;
if (graph[start][i] == 999)
continue;

int sum = graph[start][i];
DFS_VISIT(i, sum);
}
}

void DFS_VISIT(int u, int sum)
{
if (sum > min)      // do not need to do anymore
return;

node_record[u] = 1;
stack_record[top] = u;
top++;

if (u == target_node) {

printf("path node is : ");
for (int i = 1; i < top; i++)
printf("%d ", stack_record[i]);
printf("\n");

if (sum < min) {
min = sum;
}
node_record[u] = 0;
top--;

return;
}

for (int i = 1; i <= N; i++) {
if (u == i)
continue;
if (node_record[i] == 1)
continue;
if (graph[u][i] == 999)
continue;

node_record[i] = 1;
//stack_record[top] = i;
//top++;

DFS_VISIT(i, sum + graph[u][i]);

node_record[i] = 0;
//top--;
}

node_record[u] = 0;

top--;
}

int main()
{
scanf("%d %d", &N, &M);

//initialize
for (int i = 1; i <= N; i++)
for (int j = 1; j <= N; j++) {
graph[i][j] = 999;
}

for (int i = 1; i <= M; i++) {
int x, y, weight;
scanf("%d %d %d", &x, &y, &weight);

graph[x][y] = weight;
}
//end init
//

start_node = 1;
target_node = N;

DFS(start_node);

printf("min value of path is : %d\n", min);

return 0;
}




实现二:

#include <stdio.h>
#include <stdlib.h>

#define MAX 50

void DFS(int current, int distance);

int graph[MAX + 1][MAX + 1];

int node_record[MAX + 1];

int min = 999999;

int N, M;

int start_node, target_node;

int stack_record[MAX + 1];
int top = 1;

void DFS(int current, int distance)
{
if (distance > min)
return;

if (current == target_node) {
if (distance < min)
min = distance;
return;
}

for (int i = 1; i <= N; i++) {
if (graph[current][i] != 999 && node_record[i] == 0)  {
node_record[i] = 1;

//stack_record[top] = i;
//top++;

DFS(i, distance + graph[current][i]);

node_record[i] = 0;
//top--;
}
}
return ;
}

int main()
{
scanf("%d %d", &N, &M);

for (int i = 1; i <= N; i++)
for (int j = 1; j <= M; j++) {
if (i == j)
graph[i][j] = 0;
else
graph[i][j] = 999;
}

for (int i = 1; i <= M; i++) {
int x, y, weight;

scanf("%d %d %d", &x, &y, &weight);
graph[x][y] = weight;
}

start_node = 1;
target_node = N;

DFS(1, 0);

printf("min = %d\n", min);

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: