2.1 最基础的“穷竭搜索”
深度优先搜索
POJ 1979 求二维平面上能抵达区域的面积
裸的深搜
1 #include2 using namespace std; 3 4 int res, w, h; 5 char field[25][25]; 6 int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; 7 8 void dfs(int x, int y) { 9 field[x][y] = '#'; 10 res++; 11 for (int i = 0; i < 4; i++) { 12 int nx = x + dx[i], ny = y + dy[i]; 13 if (0 <= nx && nx < h && 0 <= ny && ny < w && field[nx][ny] == '.') 14 dfs(nx, ny); 15 } 16 } 17 18 int main() { 19 while (cin >> w >> h) { 20 if (w == 0 && h == 0) break; 21 int X, Y; 22 res = 0; 23 for (int i = 0; i < h; i++) { 24 for (int j = 0; j < w; j++) { 25 cin >> field[i][j]; 26 if (field[i][j] == '@') { 27 X = i; 28 Y = j; 29 } 30 } 31 } 32 dfs(X, Y); 33 cout << res << "\n"; 34 } 35 }
AOJ 0118 二维平面上,同一种类方格视为同一块,求块数
裸的深搜
1 #include2 using namespace std; 3 4 int h, w, res; 5 char board[101][101]; 6 int d[101][101]; 7 int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; 8 9 void dfs(int x, int y) { 10 d[x][y] = 1; 11 for (int i = 0; i < 4; i++) { 12 int nx = x + dx[i], ny = y + dy[i]; 13 if (nx >= 0 && nx < h && ny >= 0 && ny < w && d[nx][ny] == 0 && 14 board[nx][ny] == board[x][y]) { 15 dfs(nx, ny); 16 } 17 } 18 } 19 20 int main() { 21 while (cin >> h >> w && !(h == 0 && w == 0)) { 22 memset(d, 0, sizeof(d)); 23 res = 0; 24 for (int i = 0; i < h; i++) { 25 for (int j = 0; j < w; j++) cin >> board[i][j]; 26 } 27 for (int i = 0; i < h; i++) { 28 for (int j = 0; j < w; j++) { 29 if (d[i][j] == 0) { 30 res++; 31 dfs(i, j); 32 } 33 } 34 } 35 cout << res << "\n"; 36 } 37 }
AOJ 0033 给出10个有编号的球的输入序列,要使它们能放入两个栈中,顶端要大于底端
贪心。不妨令左边的顶端球序号更大,能放就放;否则,就放右边;如果右边还放不了,就输出NO
另解,暴力枚举球的放法。(愚蠢)
1 #include2 using namespace std; 3 4 int l, r, n, x, f; 5 6 int main() { 7 cin >> n; 8 while (n--) { 9 l = r = f = 0; 10 for (int i = 0; i < 10; i++) { 11 cin >> x; 12 if (x > l) 13 l = x; 14 else if (x > r) 15 r = x; 16 else 17 f = 1; 18 } 19 if (f) 20 cout << "NO\n"; 21 else 22 cout << "YES\n"; 23 } 24 }
POJ 3009 二维平面上有一个冰壶,一直向某一个正交方向运动(运动的第一步不能有障碍物),直到碰到障碍物(并击碎)停下,或者运动出界(视为失败)。求从起点到终点并且不大于10的步数
深搜。其中,判断障碍物与当前位置是否相邻可以计算偏移量来判断
1 #include2 #include 3 using namespace std; 4 5 int a[105][105], dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; 6 int w, h, res, sx, sy, gx, gy; 7 8 int abs(int x) { return x > 0 ? x : -x; } 9 10 void dfs(int step, int x, int y) { 11 if (step > 10 || (step > res && res != -1)) return; 12 13 for (int i = 0; i < 4; i++) { 14 int nx = x, ny = y; 15 while (nx >= 1 && nx <= h && ny >= 1 && ny <= w && a[nx][ny] == 0) 16 nx += dx[i], ny += dy[i]; 17 18 if (nx < 1 || nx > h || ny < 1 || ny > w || 19 (a[nx][ny] == 1 && abs(x - nx) + abs(y - ny) == 1)) 20 continue; 21 22 if (a[nx][ny] == 3) 23 res = step; 24 else { 25 a[nx][ny] = 0; 26 dfs(step + 1, nx - dx[i], ny - dy[i]); 27 a[nx][ny] = 1; 28 } 29 } 30 } 31 32 int main() { 33 while (scanf("%d %d", &w, &h) != EOF && w) { 34 for (int i = 1; i <= h; i++) { 35 for (int j = 1; j <= w; j++) { 36 scanf("%d", &a[i][j]); 37 if (a[i][j] == 2) sx = i, sy = j, a[i][j] = 0; 38 } 39 } 40 res = -1; 41 dfs(1, sx, sy); 42 printf("%d\n", res); 43 } 44 }
广度优先搜索
AOJ 0558 二维平面上,一只老鼠要按顺序吃掉奶酪,求最小步数
每一次寻找要吃的奶酪都用宽搜处理即可
1 #include2 using namespace std; 3 #define P pair 4 5 char board[1001][1001]; 6 int d[1001][1001]; 7 int dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1}; 8 9 int main() { 10 int h, w, n, sx, sy; 11 cin >> h >> w >> n; 12 for (int i = 1; i <= h; i++) { 13 for (int j = 1; j <= w; j++) { 14 cin >> board[i][j]; 15 if (board[i][j] == 'S') sx = i, sy = j; 16 } 17 } 18 int tar = 1, sum = 0; 19 while (tar <= n) { 20 queue que; 21 que.push(P(sx, sy)); 22 memset(d, -1, sizeof(d)); 23 d[sx][sy] = 0; 24 while (!que.empty()) { 25 P p = que.front(); 26 if (board[p.first][p.second] == '0' + tar) { 27 sx = p.first, sy = p.second; 28 sum += d[p.first][p.second]; 29 break; 30 } 31 que.pop(); 32 for (int i = 0; i < 4; i++) { 33 int nx = p.first + dx[i], ny = p.second + dy[i]; 34 if (1 <= nx && nx <= h && 1 <= ny && ny <= w && 35 board[nx][ny] != 'X' && d[nx][ny] == -1) { 36 d[nx][ny] = d[p.first][p.second] + 1; 37 que.push(P(nx, ny)); 38 } 39 } 40 } 41 tar++; 42 } 43 printf("%d\n", sum); 44 }
POJ 3669 二维平面上,有陨石会在一定时间落下。要求最小能到达安全的区域的时间
陨石落下的位置相当于在指定时间之后变成障碍物,在宽搜时加上一个判断当前时间是否小于陨石落下的时间即可
1 #include2 #include 3 #include 4 #include 5 using namespace std; 6 #define P pair 7 const int INF = 0x7f7f7f7f; 8 9 int t[310][310], d[310][310]; 10 int dx[4] = {-1, 0, 1, 0}, dy[4] = {0, 1, 0, -1}; 11 12 int main() { 13 memset(t, INF, sizeof(t)); 14 memset(d, INF, sizeof(d)); 15 int m, x, y, time; 16 scanf("%d", &m); 17 while (m--) { 18 scanf("%d %d %d", &x, &y, &t); 19 t[x][y] = min(t[x][y], time); 20 for (int i = 0; i < 4; i++) { 21 int nx = x + dx[i], ny = y + dy[i]; 22 if (nx >= 0 && ny >= 0) t[nx][ny] = min(t[nx][ny], time); 23 } 24 } 25 if (t[0][0] == 0) { 26 printf("-1\n"); 27 exit(0); 28 } 29 d[0][0] = 0; 30 queue que; 31 que.push(P(0, 0)); 32 while (!que.empty()) { 33 int a = que.front().first, b = que.front().second; 34 if (t[a][b] == INF) { 35 printf("%d\n", d[a][b]); 36 exit(0); 37 } 38 que.pop(); 39 for (int i = 0; i < 4; i++) { 40 int nx = a + dx[i], ny = b + dy[i]; 41 if (nx >= 0 && ny >= 0 && d[nx][ny] == INF && 42 d[a][b] < t[nx][ny] - 1) { 43 d[nx][ny] = d[a][b] + 1; 44 que.push(P(nx, ny)); 45 } 46 } 47 } 48 printf("-1\n"); 49 }
AOJ 0121 2×4矩阵上有1-7和一个空缺,华容道玩法,求复原需要的最小步数
常见的模型。把每一个状态映射到一个数上,从复原状态开始宽搜,预处理出所有状态的答案
1 #include2 using namespace std; 3 #define mat vector > 4 const int inf = 0x7f7f7f7f; 5 6 int vis[(int)2e7], dx[4] = {1, 0, -1, 0}, dy[4] = {0, 1, 0, -1}; 7 8 int index(mat m) { 9 int res = 0; 10 for (int i = 0; i < 2; i++) { 11 for (int j = 0; j < 4; j++) res += (1 << (3 * (i * 4 + j))) * m[i][j]; 12 } 13 return res; 14 } 15 16 int main() { 17 mat a(2); 18 vector<int> x(4); 19 a[0] = a[1] = x; 20 memset(vis, 127, sizeof(vis)); 21 22 for (int i = 0; i < 2; i++) { 23 for (int j = 0; j < 4; j++) a[i][j] = i * 4 + j; 24 } 25 26 vis[index(a)] = 0; 27 28 queue que; 29 que.push(a); 30 31 while (!que.empty()) { 32 mat top = que.front(); 33 que.pop(); 34 for (int i = 0; i < 2; i++) { 35 for (int j = 0; j < 4; j++) { 36 if (top[i][j] == 0) { 37 for (int k = 0; k < 4; k++) { 38 int nx = i + dx[k], ny = j + dy[k]; 39 if (nx >= 0 && nx < 2 && ny >= 0 && ny < 4) { 40 mat tmp = top; 41 swap(tmp[nx][ny], tmp[i][j]); 42 if (vis[index(tmp)] == inf) { 43 que.push(tmp); 44 vis[index(tmp)] = vis[index(top)] + 1; 45 } 46 } 47 } 48 break; 49 } 50 } 51 } 52 } 53 54 while (scanf("%d", &a[0][0]) != EOF) { 55 for (int i = 1; i < 4; i++) scanf("%d", &a[0][i]); 56 for (int i = 0; i < 4; i++) scanf("%d", &a[1][i]); 57 int res = vis[index(a)]; 58 printf("%d\n", res == inf ? -1 : res); 59 } 60 }
穷竭搜索
POJ 2718 给出若干个不重复的一位数,不重不漏分别组成两个数字,求这两个数的绝对值差最小值
显然,这两个数的数位差不大于1。全排列这些数,从中间分成2半,计算答案并不断更新
#include#include #include #include using namespace std; int n, a[15], Ans, t; string str; int abs(int x) { return x > 0 ? x : -x; } int min(int a, int b) { return a > b ? b : a; } int sum(int *p, int len) { if (len > 1 && p[0] == 0) return (int)1e6; int res = 0; for (int i = 0; i < len; i++) { res = res * 10 + p[i]; } return res; } int main() { scanf("%d", &t); getchar(); while (t--) { getline(cin, str); n = (str.size() + 1) / 2; for (int i = 0; i < n; i++) a[i] = str[2 * i] - '0'; sort(a, a + n); Ans = (int)1e6; do { Ans = min(Ans, abs(sum(a, n / 2) - sum(a + n / 2, n - n / 2))); } while (next_permutation(a, a + n)); printf("%d\n", Ans); } }
POJ 3187 由一个n的排列,按杨辉三角的算法得出一个结果。现给出结果,要求字典序最小的原排列
枚举所有排列验证即可。其中,排列的第 i 项(从1算起)乘以C(n - 1, i - 1)累加就是答案。证明可以考虑某一位置的数是怎样对最终结果产生贡献,即,在三角形中每有一条路径从该数到结果,该数对结果的贡献就有1倍。那么这样的路径一共有C(n - 1, i - 1)条,因为向左下/右下一定有n - i步,其中有i - 1步是向左下
1 #include2 #include 3 #include 4 using namespace std; 5 6 int c[15], a[15]; 7 int n, sum; 8 9 int main() { 10 scanf("%d %d", &n, &sum); 11 c[1] = 1; 12 for (int i = 2; i <= n; i++) c[i] = c[i - 1] * (n - i + 1) / (i - 1); 13 for (int i = 1; i <= n; i++) a[i] = i; 14 do { 15 int r = 0; 16 for (int i = 1; i <= n; i++) r += c[i] * a[i]; 17 if (r == sum) { 18 for (int i = 1; i <= n; i++) 19 printf("%d%c", a[i], i == n ? '\n' : ' '); 20 exit(0); 21 } 22 } while (next_permutation(a + 1, a + n + 1)); 23 }
POJ 3050 5×5的矩阵,从一点出发走5步,经过的6个格子组成一个数,求数的可能情况有多少种
深搜模拟一下过程即可
1 #include2 #include 3 #include 4 using namespace std; 5 6 int c[15], a[15]; 7 int n, sum; 8 9 int main() { 10 scanf("%d %d", &n, &sum); 11 c[1] = 1; 12 for (int i = 2; i <= n; i++) c[i] = c[i - 1] * (n - i + 1) / (i - 1); 13 for (int i = 1; i <= n; i++) a[i] = i; 14 do { 15 int r = 0; 16 for (int i = 1; i <= n; i++) r += c[i] * a[i]; 17 if (r == sum) { 18 for (int i = 1; i <= n; i++) 19 printf("%d%c", a[i], i == n ? '\n' : ' '); 20 exit(0); 21 } 22 } while (next_permutation(a + 1, a + n + 1)); 23 }
AOJ 0525 R×C(1≤R≤10,1≤C≤10000)矩阵中数字只有1和0两种取值,可以对同一行或同一列的数字翻转,操作任意次,输出所有情况中0的数量的最大值
注意到R很小,枚举每一行的翻转情况。此时每一列的情况只有翻与不翻,取其中的最大值,累加,就是当前行的翻转情况下的最大值。时间复杂度O(2 ^ R × R × C)
1 #include2 using namespace std; 3 #define inc(i, l, r) for (int i = l; i <= r; i++) 4 5 const int maxn = 1e6 + 5; 6 7 int a[15][10005], c[15][10005]; 8 int w, h, res, ans; 9 10 int main() { 11 while (scanf("%d%d", &h, &w) != EOF && h) { 12 ans = 0; 13 for (int i = 0; i < h; i++) { 14 for (int j = 0; j < w; j++) scanf("%d", &a[i][j]); 15 } 16 for (int i = 0; i < (1 << h); i++) { 17 int res = 0; 18 for (int j = 0; j < h; j++) { 19 if ((i >> j) & 1) 20 for (int k = 0; k < w; k++) c[j][k] = a[j][k]; 21 else 22 for (int k = 0; k < w; k++) c[j][k] = 1 - a[j][k]; 23 } 24 for (int j = 0; j < w; j++) { 25 int tmp = 0; 26 for (int k = 0; k < h; k++) tmp += c[k][j]; 27 res += max(tmp, h - tmp); 28 } 29 ans = max(ans, res); 30 } 31 printf("%d\n", ans); 32 } 33 }
END