dfs
来源:互联网 发布:冒泡排序法c语言 编辑:程序博客网 时间:2024/06/05 12:55
问题一:
问题描述: 输入一个数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 50int 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 20int box[MAX]; //number in the boxint 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 50int book[MAX][MAX]; //tagint 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};//matrixint N, M;// target int final_x;int final_y;// countstatic 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 81 2 21 5 102 3 32 5 73 1 43 4 44 5 55 3 3
第一行的输入表示5个城市,8代表边的条数
接下来的数据a b c 分别代表城市编号以及城市之间的距离数。
现在要求出起点(1)到终点(5)的最短路径?
分析:
可以用dfs也可以用bfs来实现。
先考虑dfs:
因为图中存在环路,有向图(无向图也一样),我们用dfs进行遍历的时候,可以用一个变量 min 来记录当前路径的代价,再用一个标识数组 (比如book)来标识当前路径上已经选择的节点,这样就能正确处理图中存在环的情况。
实现一:
#include <stdio.h>#include <stdlib.h>#define MAX 50void DFS(int start);void DFS_VISIT(int u, int sum);int graph[MAX + 1][MAX + 1]; //graphint node_record[MAX + 1]; //node identifyint stack_record[MAX + 1];int top = 1;int N, M; // N * Mint min = 999999;int start_node; //startint target_node; //endvoid 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 50void 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;}
- DFS
- DFS
- dfs
- dfs
- dfs
- dfs
- DFS
- DFS
- dfs
- DFS
- DFS
- DFS
- dfs
- DFS
- dfs
- dfs
- dfs
- dfs
- MySQL的if,case语句
- jQuery获取Select选择的Text和 Value(转)
- iOS8 沙盒路径变化特性
- Linux内核驱动之read和write
- 安装RedHat Linux引导盘安装服务器提示GPT分区无法继续安装的解决方法
- dfs
- php bootstrap 实现简单登录
- 纯代码创建按钮
- 【转】java.lang.Exception: Socket bind failed: [730048]问题解决办法
- svn在linux下的使用(svn命令)
- UITextView - 6
- SQL 分类条件统计
- Compiler Options Listed by Category
- android手机分辨率和设计图尺寸之间的关系