《挑战程序设计竞赛》课后练习题解集——2.1 最基础的“穷竭搜索”

2.1 最基础的“穷竭搜索”

深度优先搜索

POJ 1979  求二维平面上能抵达区域的面积

裸的深搜

 1 #include <iostream>
 2 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 }
View Code

AOJ 0118  二维平面上,同一种类方格视为同一块,求块数

裸的深搜

 1 #include <bits/stdc++.h>
 2 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 }
View Code

AOJ 0033  给出10个有编号的球的输入序列,要使它们能放入两个栈中,顶端要大于底端

贪心。不妨令左边的顶端球序号更大,能放就放;否则,就放右边;如果右边还放不了,就输出NO

另解,暴力枚举球的放法。(愚蠢)

 1 #include <bits/stdc++.h>
 2 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 }
View Code

POJ 3009  二维平面上有一个冰壶,一直向某一个正交方向运动(运动的第一步不能有障碍物),直到碰到障碍物(并击碎)停下,或者运动出界(视为失败)。求从起点到终点并且不大于10的步数

深搜。其中,判断障碍物与当前位置是否相邻可以计算偏移量来判断

 1 #include <cstdio>
 2 #include <iostream>
 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 }
View Code

广度优先搜索

AOJ 0558 二维平面上,一只老鼠要按顺序吃掉奶酪,求最小步数

每一次寻找要吃的奶酪都用宽搜处理即可

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define P pair<int, int>
 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<P> 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 }
View Code

POJ 3669  二维平面上,有陨石会在一定时间落下。要求最小能到达安全的区域的时间

陨石落下的位置相当于在指定时间之后变成障碍物,在宽搜时加上一个判断当前时间是否小于陨石落下的时间即可

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <queue>
 5 using namespace std;
 6 #define P pair<int, int>
 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<P> 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 }
View Code

AOJ 0121  2×4矩阵上有1-7和一个空缺,华容道玩法,求复原需要的最小步数

常见的模型。把每一个状态映射到一个数上,从复原状态开始宽搜,预处理出所有状态的答案

 1 #include <bits/stdc++.h>
 2 using namespace std;
 3 #define mat vector<vector<int> >
 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<mat> 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 }
View Code

穷竭搜索

 POJ 2718 给出若干个不重复的一位数,不重不漏分别组成两个数字,求这两个数的绝对值差最小值

显然,这两个数的数位差不大于1。全排列这些数,从中间分成2半,计算答案并不断更新

#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
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);
    }
}
View Code

POJ 3187  由一个n的排列,按杨辉三角的算法得出一个结果。现给出结果,要求字典序最小的原排列

枚举所有排列验证即可。其中,排列的第 i 项(从1算起)乘以C(n - 1, i - 1)累加就是答案。证明可以考虑某一位置的数是怎样对最终结果产生贡献,即,在三角形中每有一条路径从该数到结果,该数对结果的贡献就有1倍。那么这样的路径一共有C(n - 1, i - 1)条,因为向左下/右下一定有n - i步,其中有i - 1步是向左下

 1 #include <algorithm>
 2 #include <cstdio>
 3 #include <iostream>
 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 }
View Code

POJ 3050 5×5的矩阵,从一点出发走5步,经过的6个格子组成一个数,求数的可能情况有多少种

深搜模拟一下过程即可

 1 #include <algorithm>
 2 #include <cstdio>
 3 #include <iostream>
 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 }
View Code

AOJ 0525  R×C(1≤R≤10,1≤C≤10000)矩阵中数字只有1和0两种取值,可以对同一行或同一列的数字翻转,操作任意次,输出所有情况中0的数量的最大值

 注意到R很小,枚举每一行的翻转情况。此时每一列的情况只有翻与不翻,取其中的最大值,累加,就是当前行的翻转情况下的最大值。时间复杂度O(2 ^ R × R × C)

 1 #include <bits/stdc++.h>
 2 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 }
View Code

 END

猜你喜欢

转载自www.cnblogs.com/hs-zlq/p/12090919.html