深度优先搜索(DFS)
回溯,剪枝。想清楚搜索顺序 。回溯时注意恢复现场。
搜索框架
排列数字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <iostream> using namespace std;const int N = 10 ;bool st[N];int n;int path[N];void dfs (int u) { if (u == n) { for (int i=0 ;i<n;i++) cout << path[i] << " " ; cout << endl; return ; } for (int i=1 ;i<=n;i++) { if (!st[i]) { st[i] = true ; path[u] = i; dfs (u+1 ); st[i] = false ; } } } int main () { cin >> n; dfs (0 ); return 0 ; }
第一种搜索方式
遍历每一行,考虑将皇后放在第几列上。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 #include <iostream> using namespace std;const int N = 20 ;bool col[N], dg[N], udg[N];int n;char g[N][N];void dfs (int u) { if (u == n) { for (int i=0 ;i<n;i++) puts (g[i]); puts ("" ); return ; } for (int i=0 ;i<n;i++) { if (!col[i] && !dg[u+i]&&!udg[n-u+i]) { g[u][i] = 'Q' ; col[i] = dg[u+i] = udg[n-u+i] = true ; dfs (u+1 ); col[i] = dg[u+i] = udg[n-u+i] = false ; g[u][i] = '.' ; } } } int main () { cin >> n; for (int i=0 ;i<n;i++) { for (int j=0 ;j<n;j++) { g[i][j] = '.' ; } } dfs (0 ); return 0 ; }
第二种搜索方式
从上到下,从左到右依次考虑每个格子放不放皇后。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <iostream> using namespace std;const int N = 20 ;char g[N][N];int row[N], col[N], dg[N], udg[N];int n;void dfs (int x, int y, int s) { if (y == n) y=0 , x++; if (x == n) { if (s == n) { for (int i=0 ;i<n;i++) { for (int j=0 ;j<n;j++) { cout << g[i][j]; } cout << endl; } cout << endl; } return ; } dfs (x, y+1 , s); if (!row[x] && !col[y] && !dg[x+y] && !udg[x-y+n]) { row[x] = col[y] = dg[x+y] = udg[x-y+n] = true ; g[x][y] = 'Q' ; dfs (x, y+1 , s+1 ); g[x][y] = '.' ; row[x] = col[y] = dg[x+y] = udg[x-y+n] = false ; } } int main () { cin >> n; for (int i=0 ;i<n;i++) { for (int j=0 ;j<n;j++) { g[i][j] = '.' ; } } dfs (0 ,0 ,0 ); return 0 ; }
宽度优先搜索(BFS)
BFS 可以搜索边权相同的图中的最短路。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <iostream> #include <cstring> #include <queue> using namespace std;const int N = 110 ;typedef pair<int , int > PII;int g[N][N];int d[N][N];PII q[N * N]; int n, m;int bfs () { int hh = 0 , tt = 0 ; q[0 ] = {0 ,0 }; memset (d, -1 , sizeof d); d[0 ][0 ] = 0 ; int dx[4 ] = {-1 ,0 ,1 ,0 }, dy[4 ] = {0 ,1 ,0 ,-1 }; while (hh <= tt) { auto t = q[hh++]; for (int i=0 ;i<4 ;i++) { int x = t.first + dx[i], y = t.second + dy[i]; if (x>=0 && x<n && y>=0 && y<m && d[x][y] == -1 && g[x][y] == 0 ) { d[x][y] = d[t.first][t.second] + 1 ; q[++tt] = {x, y}; } } } return d[n-1 ][m-1 ]; } int main () { cin >> n >> m; for (int i=0 ;i<n;i++) { for (int j=0 ;j<m;j++) { cin >> g[i][j]; } } cout << bfs () << endl; return 0 ; }
DFS 与 BFS 对比
算法
数据结构
空间
优点
DFS
栈
O ( h ) O(h) O ( h )
路径不具备最短性
BFS
队列
O ( 2 h ) O(2^{h}) O ( 2 h )
可以搜索最短路
树和图
树 是一种特殊的图,无环连通图。
图分为有向图 和无向图 。
无向图 是一种特殊的有向图。可以看成两条边互相连通的有向图。
图的存储
邻接矩阵
适合存储稠密图,空间复杂度为 O ( n 2 ) O(n^{2}) O ( n 2 ) 。n 为节点数量。
邻接表
每个节点都对应一个单链表 ,存储与当前节点相连的其他节点 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 图: 2 ----------> 4 | \ ^ ^ | \ / | | \ / | | / \ | | / \ | v / v | 1 ----------> 3 邻接表: h[1]: ---> 3 ---> 4 ---> ø h[2]: ---> 1 ---> 4 ---> ø h[3]: ---> 4 ---> ø h[4]: ---> ø
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <iostream> #include <cstring> using namespace std;const int N = 1e5 + 10 , M = N*2 ;int n,m;int h[N], e[M], ne[M], idx;bool st[N];int ans = N;void add (int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx++; } int dfs (int u) { st[u] = true ; int sum = 1 , res = 0 ; for (int i=h[u];i!=-1 ;i=ne[i]) { int j = e[i]; if (!st[j]) { int s = dfs (j); res = max (res, s); sum+=s; } } res = max (n-sum, res); ans = min (res, ans); return sum; } int main () { cin >> n; memset (h, -1 , sizeof h); for (int i=0 ; i<n-1 ; i++) { int a,b; cin >>a >> b; add (a,b); add (b,a); } dfs (1 ); cout << ans << endl; return 0 ; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <iostream> #include <cstring> using namespace std;const int N = 100010 ;int n,m;int h[N], e[N], ne[N], idx;int d[N], q[N];void add (int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx++; } int bfs () { int hh = 0 , tt = 0 ; q[0 ] = 1 ; memset (d, -1 , sizeof d); d[1 ] = 0 ; while (hh <= tt) { int t = q[hh++]; for (int i=h[t]; i!=-1 ; i=ne[i]) { int j = e[i]; if (d[j] == -1 ) { d[j] = d[t] + 1 ; q[++tt] = j; } } } return d[n]; } int main () { cin >> n >> m; memset (h, -1 , sizeof h); for (int i=0 ;i<m;i++) { int a, b; cin >> a >> b; add (a, b); } cout << bfs () << endl; return 0 ; }
拓扑排序
有向无环图 也称为拓扑图 。
如果有环,那一定没有拓扑序。
拓扑序不一定唯一 。
若一个由图中所有点构成的序列 A 满足:对于图中的每条边 (x, y),x 在 A 中都出现在 y 之前,则称 A 是该图的一个拓扑序列 。
有向图的拓扑序列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 #include <cstring> #include <iostream> #include <algorithm> using namespace std;const int N = 1e5 + 10 ;int q[N], d[N];int e[N], ne[N], h[N], idx;int n, m;void add (int a, int b) { e[idx] = b; ne[idx] = h[a]; h[a] = idx++; } bool toposort () { int hh = 0 , tt = -1 ; for (int i=1 ;i<=n;i++) { if (!d[i]) { q[++tt] = i; } } while (hh<=tt) { int t = q[hh++]; for (int i = h[t]; i != -1 ; i = ne[i]) { int j = e[i]; d[j]--; if (!d[j]) { q[++tt] = j; } } } return tt == n-1 ; } int main () { cin >> n >> m; memset (h, -1 , sizeof h); for (int i=0 ;i<m;i++) { int a, b; cin >> a >> b; add (a, b); d[b]++; } if (toposort ()) { for (int i=0 ;i<n;i++) cout << q[i] << " " ; cout << endl; } else { puts ("-1" ); } return 0 ; }