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; }
相关文章推荐
- 从零学Android(十三)、Android中的数据存储方式简介
- Android Touch事件传递机制通俗讲解
- Java学习笔记-适配器用法
- 将Sublime Text 2搭建成一个好用的IDE(转)
- Play Framework 项目遇到问题
- 代码高亮
- win10系统运行帝国时代2提示错误代码0xc0000022的原因及解决方法
- 工厂方法模式的结构
- 安卓ViewPager--OnPageChangeListener
- Linux软硬连接
- IOS瀑布流通过UICollectionView控件实现
- c# 将dwg文件转化为pdf
- JS遍历属性和方法
- 如何导出P12文件
- 【Java基础】正确使用 Volatile 变量
- 定制CRM需要做哪些准备?
- 用Flex创建的项目移动文件夹就不能运行了
- Android 系统源码分析之View(一)
- leetcode :Candy
- NGUI UILabel 富文本格式说明