《王道论坛计算机考研机试指南》第六章【搜索】

44 篇文章 1 订阅

一、枚举

百鸡问题

#include <iostream>
#include <cstdio>
using namespace std;
int main() {
    int n;
    while (scanf("%d", &n) != EOF) {
        for (int x = 0; x <= 100; x++) {
            for (int y = 0; y <= 100-x; y++) {
                int z = 100 -x - y;
                if (15*x+9*y+z <= 3*n) {
                    printf("x=%d,y=%d,z=%d\n", x, y, z);
                }
            }
        }
    }
    return 0;
}

二、广度优先搜索(BFS)

胜利大逃亡

#include <cstdio>
#include <queue>
#include <iostream>
using namespace std;
bool mark[50][50][50];   // 标记数组
int maze[50][50][50];     // 保存立方体信息
struct N {      // 状态结构体
    int x, y, z;
    int t;
};
queue<N> Q;  // 队列,队列中的元素为状态
// 坐标变换数组,由坐标(x, y, z)扩展得到的新坐标均可通过(x+go[i][0], y+go[i][1], z+go[i][2])得到
int go[][3] = {1, 0, 0,
              -1, 0, 0,
               0, 1, 0,
               0, -1, 0,
               0, 0, 1,
               0, 0, -1};
int BFS(int a, int b, int c) {  // 广度优先搜索,返回最短时间
    while (!Q.empty()) {
        N now = Q.front();   // 得到队头状态
        Q.pop();      // 从队列中弹出队头元素
        for (int i = 0; i < 6; i++) {    // 依次扩展六个相邻结点
            int nx = now.x + go[i][0];
            int ny = now.y + go[i][1];
            int nz = now.z + go[i][2];     // 计算新坐标
            if (nx < 0 || nx >= a || ny < 0 || ny >= b || nz < 0 || nz >= c)
                continue;
            if (maze[nx][ny][nz] == 1)   // 若该位置为墙 丢弃该坐标
                continue;
            if (mark[nx][ny][nz] == true)   // 若该位置已经经过过 丢弃该状态
                continue;
            N tmp;     // 新的状态
            tmp.x = nx;
            tmp.y = ny;
            tmp.z = nz;    // 新状态包含的坐标
            tmp.t = now.t+1;    // 新状态的耗时
            Q.push(tmp);       // 将该状态放入队列
            mark[nx][ny][nz] = true;    // 标记该坐标
            if (nx == a-1 && ny == b-1 && nz == c-1)
                return tmp.t;   // 若该坐标即为终点 可直接返回其耗时
        }
    }
    return -1;
}
int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        int a, b, c, t;
        scanf("%d%d%d%d", &a, &b, &c, &t);
        for (int i = 0; i < a; i++) {
            for (int j = 0; j < b; j++) {
                for (int k = 0; k < c; k++ ) {
                    scanf("%d", &maze[i][j][k]);   // 输入立方体信息
                    mark[i][j][k] = false;     // 初始化标记数组
                }
            }
        }
        while (!Q.empty())
            Q.pop();
        mark[0][0][0] = true;   // 标记起点
        N tmp;
        tmp.x = tmp.y = tmp.z = 0;   // 初始状态
        tmp.t = 0;
        Q.push(tmp);
        int rec = BFS(a, b, c);
        if (rec <= t)
            printf("%d\n", rec);
        else
            printf("-1\n");
    }
    return 0;
}

非常可乐

#include <cstdio>
#include <queue>
#include <iostream>
using namespace std;
bool mark[101][101][101];   // 标记数组
struct N {      // 状态结构体
    int a, b, c;
    int t;
};
queue<N> Q;  // 队列
// 倾倒函数,由体积为sa的杯子倒往容积为sb的杯子,其中引用参数a和b
// 初始时为原始杯子中可乐的体积,当函数调用完毕时,为各自杯子中的新体积
void AtoB(int &a, int sa, int &b, int sb) {
    if (sb - b >= a) {   // 若a可以全部倒到b中
        b += a;
        a = 0;
    } else {
        a -= sb - b;
        b = sb;
    }
}
int BFS(int s, int n, int m) {
    while (!Q.empty()) {
        N now = Q.front();   // 得到队头状态
        Q.pop();      // 从队列中弹出队头元素
        int a, b, c;
        a = now.a;
        b = now.b;
        c = now.c;
        AtoB(a, s, b, n);    // a -> b
        if (mark[a][b][c] == false) {
            mark[a][b][c] = true;
            N tmp;
            tmp.a = a;
            tmp.b = b;
            tmp.c = c;
            tmp.t = now.t+1;
            if (a == s/2 && b == s/2) return tmp.t;
            if (b == s/2 && c == s/2) return tmp.t;
            if (a == s/2 && c == s/2) return tmp.t;
            Q.push(tmp);
        }
        a = now.a;
        b = now.b;
        c = now.c;
        AtoB(b, n, a, s);    // b -> a
        if (mark[a][b][c] == false) {
            mark[a][b][c] = true;
            N tmp;
            tmp.a = a;
            tmp.b = b;
            tmp.c = c;
            tmp.t = now.t+1;
            if (a == s/2 && b == s/2) return tmp.t;
            if (b == s/2 && c == s/2) return tmp.t;
            if (a == s/2 && c == s/2) return tmp.t;
            Q.push(tmp);
        }

        a = now.a;
        b = now.b;
        c = now.c;
        AtoB(a, s, c, m);    // a -> c
        if (mark[a][b][c] == false) {
            mark[a][b][c] = true;
            N tmp;
            tmp.a = a;
            tmp.b = b;
            tmp.c = c;
            tmp.t = now.t+1;
            if (a == s/2 && b == s/2) return tmp.t;
            if (b == s/2 && c == s/2) return tmp.t;
            if (a == s/2 && c == s/2) return tmp.t;
            Q.push(tmp);
        }

        a = now.a;
        b = now.b;
        c = now.c;
        AtoB(c, m, a, s);     // c -> a
        if (mark[a][b][c] == false) {
            mark[a][b][c] = true;
            N tmp;
            tmp.a = a;
            tmp.b = b;
            tmp.c = c;
            tmp.t = now.t+1;
            if (a == s/2 && b == s/2) return tmp.t;
            if (b == s/2 && c == s/2) return tmp.t;
            if (a == s/2 && c == s/2) return tmp.t;
            Q.push(tmp);
        }

        a = now.a;
        b = now.b;
        c = now.c;
        AtoB(b, n, c, m);     // b -> c
        if (mark[a][b][c] == false) {
            mark[a][b][c] = true;
            N tmp;
            tmp.a = a;
            tmp.b = b;
            tmp.c = c;
            tmp.t = now.t+1;
            if (a == s/2 && b == s/2) return tmp.t;
            if (b == s/2 && c == s/2) return tmp.t;
            if (a == s/2 && c == s/2) return tmp.t;
            Q.push(tmp);
        }

        a = now.a;
        b = now.b;
        c = now.c;
        AtoB(c, m, b, n);      // c -> b
        if (mark[a][b][c] == false) {
            mark[a][b][c] = true;
            N tmp;
            tmp.a = a;
            tmp.b = b;
            tmp.c = c;
            tmp.t = now.t+1;
            if (a == s/2 && b == s/2) return tmp.t;
            if (b == s/2 && c == s/2) return tmp.t;
            if (a == s/2 && c == s/2) return tmp.t;
            Q.push(tmp);
        }
    }
    return -1;
}

int main() {
    int s, n, m;
    while (scanf("%d%d%d", &s, &n, &m) == 3) {
        if (s == 0) break;
        if (s & 1) {
            printf("NO\n");
            continue;
        }
        memset(mark, false, sizeof(mark));
        while (!Q.empty())
            Q.pop();
        mark[s][0][0] = true;
        N tmp;
        tmp.a = s;
        tmp.b = 0;
        tmp.c = 0;   // 初始状态
        tmp.t = 0;
        Q.push(tmp);
        int rec = BFS(s, n, m);
        if (rec == -1)
            printf("NO\n");
        else
            printf("%d\n", rec);

    }
    return 0;
}

三、递归

汉诺塔III

#include <cstdio>
using namespace std;
long long F(int num) {  // 递归函数,返回值较大使用long long类型
    if (num == 1)
        return 2;    // 当参数为1时直接返回2
    return 3 * F(num-1) + 2;   // 否则递归调用F(num-1)
}
int main() {
    int n;
    while (scanf("%d", &n) != EOF) {
        printf("%lld", F(n));
    }
    return 0;
}

四、递归的应用

回溯法枚举

Prime Ring Problem

#include <cstdio>
#include <iostream>
using namespace std;
int ans[20];
bool has[20];
int n;
int prime[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41};
bool isPrime(int x) {
    for (int i = 0; i < 13; i++)
        if (x == prime[i])
            return true;
    return false;
}
void check() {
    if (isPrime(ans[n] + ans[1]) == false)
        return;
    for (int i = 1; i <= n; i++) {
        if (i != 1)
            printf(" ");
        printf("%d", ans[i]);
    }
    printf("\n");
}
void DFS(int num) {
    if (num > 1) {
        if (isPrime(ans[num] + ans[num-1]) == false)
            return;
    }
    if (num == n) {
        check();
        return;
    }
    for (int i = 2; i <= n; i++) {
        if (!has[i]) {
            has[i] = true;
            ans[num+1] = i;
            DFS(num+1);
            has[i] = false;
        }
    }
}
int main() {
    int cas = 1;
    while (scanf("%d", &n) != EOF) {
        printf("Case %d:\n", cas++);
        memset(has, false, sizeof(has));
        ans[1] = 1;
        has[1] = true;
        DFS(1);
        printf("\n");
    }
    return 0;
}
图的遍历

Oil Deposits

#include <iostream>
#include <cstdio>
using namespace std;
int m ,n;
bool mark[105][105];
char maze[105][105];
int go[][2] = {1, 0, -1, 0, 0, 1, 0, -1, 1, 1, 1, -1, -1, 1, -1, -1};
void DFS(int x, int y) {
    for (int i = 0; i < 8; i++) {
        int nx = x + go[i][0];
        int ny = y + go[i][1];
        if (nx < 0 || nx >= m || ny < 0 || ny >= n)
            continue;
        if (maze[nx][ny] == '@' && !mark[nx][ny]) {
            mark[nx][ny] = true;
            DFS(nx, ny);
        }
    }
}
int main() {
    while (scanf("%d%d", &m, &n) == 2 && m != 0) {
        memset(mark, false, sizeof(mark));
        int ans = 0;
        for (int i = 0; i < m; i++) {
            scanf("%s", maze[i]);
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (maze[i][j] == '@' && !mark[i][j]) {
                    DFS(i, j);
                    ans++;
                }
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}

二、深度优先搜索(DFS)

Tempter of the Bone

#include <iostream>
#include <cstdio>
using namespace std;
int m ,n, t;       // 地图大小为m*n,从起点到终点能否恰好t秒
char maze[8][8];   // 保存地图信息
int go[][2] = {1, 0, -1, 0, 0, 1, 0, -1};   // 四个方向移动坐标差
int sx, sy, ex, ey;   // 起点和终点的x,y坐标
bool success;    // 是否找到的标记
void DFS(int x, int y, int time) {   // 递归形式的深度优先搜索
    for (int i = 0; i < 4; i++) {
        int nx = x + go[i][0];
        int ny = y + go[i][1];      // 计算其坐标
        if (nx < 0 || nx >= m || ny < 0 || ny >= n)    // 若坐标在地图外
            continue;
        if (maze[nx][ny] == 'X')     // 若该位置为墙,跳过
            continue;
        if (maze[nx][ny] == 'D') {    // 若该位置为门
            if (time + 1 == t) {     // 所用时间为恰好为t
                success = true;      // 搜索成功
                return;
            } else
                continue;      // 否则该状态的后续状态不可能为答案(经过的点不能再经过),跳过
        }
        maze[nx][ny] = 'X';   // 该状态扩展而来的后续状态中,该位置都不能被经过,直接修改该位置为墙
        DFS(nx, ny, time+1);  // 递归扩展该状态,所用时间递增
        maze[nx][ny] = '.';   // 若其后续状态全部遍历完毕,则退回上层状态(因为要搜索其后续状态而改成墙的位置,现改回普通位置)
        if (success)
            return;
    }
}
int main() {
    while (scanf("%d%d%d", &m, &n, &t) == 3) {
        if (m == 0 && n == 0 && t == 0)
            break;
        success = false;
        for (int i = 0; i < m; i++) {
            scanf("%s", maze[i]);
        }
        for (int i = 0; i < m; i++) {
            for (int j = 0; j < n; j++) {
                if (maze[i][j] == 'S') {   // 起点位置
                    sx = i;
                    sy = j;
                }
                if (maze[i][j] == 'D') {   // 门的位置
                    ex = i;
                    ey = j;
                }
            }
        }
        if ((sx+sy) % 2 == ((ex+ey)%2 + t%2) % 2) {  // 找到起点和终点后,先判断S和D位置的奇偶性,是否和t符合,若不符合直接跳过搜索
            maze[sx][sy] = 'X';   // 将起点标记为墙!!!刚开始忘了标记
            DFS(sx, sy, 0);       // 递归扩展初始状态
        }
        if (success)
            printf("YES\n");
        else
            printf("NO\n");
    }
    return 0;
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值