简单搜索专题

0.序言

这是一篇记录简单搜索(不需要复杂剪枝)题目的题解合集,题单参考的是著名的《kuangbin带你飞》,由于HDU近期无法访问,所以下面不会出现HDU上的题目

1.POJ 3278 Catch That Cow

题目

POJ 3278

分析

简单bfs

代码
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;

const int MAX_N = 1000000 + 5;
int cnt[MAX_N];

int bfs(int st, int en)
{
    memset(cnt, -1, sizeof(cnt));
    queue<int> q;
    q.push(st);
    cnt[st] = 0;
    while(!q.empty()) {
	int u = q.front();
	q.pop();
	if(u == en)
	    return cnt[u];
	int a = u * 2, b = u + 1, c = u - 1;
	if(a <= MAX_N && cnt[a] == -1){
	    q.push(a);
	    cnt[a] = cnt[u] + 1;
	}
	if(b <= MAX_N && cnt[b] == -1){
	    q.push(b);
	    cnt[b] = cnt[u] + 1;
	}
	if(c >= 0 && cnt[c] == -1){
	    q.push(c);
	    cnt[c] = cnt[u] + 1;
	}
    }
    return 0;
}

int main()
{
    int n, k;
    scanf("%d%d", &n, &k);
    printf("%d\n", bfs(n, k));
    return 0;
}

2.POJ 3984 迷宫问题

题目

POJ 3984

分析

要求的是最短路线,所以考虑用BFS,枚举下一步的四个方向并尝试

由于要记录路径,所以我们用一个数组 pre[i][j] 记录在到达 \((i,j)\) 前到达的点,在找到完整路径后反向输出即可

#include<cstdio>
#include<queue>
#include<stack>
#include<cstring>
using namespace std;

struct node{
    int x, y;
};
int mat[6][6];
bool vis[6][6];
int dx[] = {1, 0, -1, 0};
int dy[] = {0, 1, 0, -1};
node pre[6][6];
queue<node> q;

int main()
{
    for(int i = 1; i <= 5; i++)
        for(int j = 1; j <= 5; j++)
            scanf("%d", &mat[i][j]);
    pre[1][1] = {0, 0};
    vis[1][1] = true;
    q.push({1, 1});
    while(!q.empty()) {
        node t = q.front();
        q.pop();
        if(t.x == 5 && t.y == 5) {
            stack<node> ans;
            while(t.x) {
                ans.push(t);
                t = pre[t.x][t.y];
            }
            while(!ans.empty()) {
                t = ans.top();
                printf("(%d, %d)\n", t.x - 1, t.y - 1);
                ans.pop();
            }
            break;
        }
        for(int i = 0; i < 4; i++) {
            int nx = dx[i] + t.x, ny = dy[i] + t.y;
            if(nx < 1 || nx > 5 || ny < 1 || ny > 5 || vis[nx][ny] || mat[nx][ny])
                continue;
            pre[nx][ny] = t;
            vis[nx][ny] = true;
            q.push({nx, ny});
        }
    }
    return 0;
}

3.POJ 2251 Dungeon Master

题目

POJ 2251

分析

实际上这道题就是三维的迷宫问题,二维时下一步有四种情况,三维时多了两种向上和向下

代码
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;

struct node{
    int x, y, z;
    int step;
};
bool vis[35][35][35];
char mat[35][35][35];
int l, r, c;
int st_x, st_y, st_z;
int dx[] = {0, 0, 0, 0, 1, -1};
int dy[] = {0, 1, 0, -1, 0, 0};
int dz[] = {1, 0, -1, 0, 0, 0};

int bfs()
{
    vis[st_x][st_y][st_z] = true;
    queue<node> q;
    q.push({st_x, st_y, st_z, 0});
    while(!q.empty()) {
        node t = q.front();
        q.pop();
        if(mat[t.x][t.y][t.z] == 'E')
            return t.step;
        for(int i = 0; i < 6; i++) {
            int nx = t.x + dx[i], ny = t.y + dy[i], nz = t.z + dz[i];
            if(nx >= 1 && nx <= l && ny >= 1 && ny <= r && nz >= 1 && nz <= c && !vis[nx][ny][nz] && mat[nx][ny][nz] != '#') {
                vis[nx][ny][nz] = true;
                q.push({nx, ny, nz, t.step + 1});
            }
        }
    }
    return -1;
}

int main()
{
    cin >> l >> r >> c;
    while(l) {
        for(int i = 1; i <= l; i++) {
            for(int j = 1; j <= r; j++) {
                for(int k = 1; k <= c; k++) {
                    cin >> mat[i][j][k];
                    if(mat[i][j][k] == 'S') {
                        st_x = i;
                        st_y = j;
                        st_z = k;
                    }
                }
            }
        }
        memset(vis, 0, sizeof(vis));
        int ans = bfs();
        if(ans == -1)
            cout << "Trapped!" << endl;
        else
            cout << "Escaped in " << ans << " minute(s)." << endl;
        cin >> l >> r >> c;
    }
    return 0;
}

4.POJ 3126 Prime Path

题目

POJ 3126

分析

由于要用素数组成一个路径,所以我们先用线性筛筛出 \(1000\)\(9999\) 之间的素数

对于代表当前状态的一个数 \(\overline{abcd}\) 我们可以将个位,十位,百位,或千位更改成另一个数,使新的四位数为素数,从而得到下一个状态,这样就转化为了一个搜索问题,可以用bfs解决

代码
#include<cstdio>
#include<cstring>
#include<queue>
using namespace std;

const int MAX_N = 10000 + 5;
int prime[MAX_N];
int ans[MAX_N];
bool is_prime[MAX_N];
int cnt;
int st, en;

void sieve(int n)
{
    memset(is_prime, 1, sizeof(is_prime));
    is_prime[1] = false;
    cnt = 0;
    for(int i = 2; i <= n; i++) {
        if(is_prime[i])
            prime[++cnt] = i;
        for(int j = 1; j <= cnt && i * prime[j] <= n; j++) {
            is_prime[i * prime[j]] = false;
            if(i % prime[j] == 0)
                break;
        }
    }
}

int f(int num, int idx, int nw)
{
    int pw[] = {1, 10, 100, 1000, 10000};
    return num / pw[idx] * pw[idx] + nw * pw[idx - 1] + num % pw[idx - 1];
}

int bfs()
{
    if(st == en)
        return 0;
    queue<int> q;
    q.push(st);
    ans[st] = 0;
    while(!q.empty()) {
        int t = q.front();
        q.pop();
        for(int i = 1; i <= 4; i++) {
            for(int j = (i == 4 ? 1 : 0); j <= 9; j++) {
                int nw = f(t, i, j);
                if(ans[nw] == -1 && is_prime[nw]) {
                    ans[nw] = ans[t] + 1;
                    if(nw == en)
                        return ans[nw];
                    q.push(nw);
                }
            }
        }
    }
    return 0;
}

int main()
{
    int t;
    scanf("%d", &t);
    sieve(10000);
    while(t--) {
        scanf("%d%d", &st, &en);
        memset(ans, -1, sizeof(ans));
        printf("%d\n", bfs());
    }
    return 0;
}

5.POJ 3414 Pots

题目

POJ 3414

分析

可以用 \((x,y)\) 表示两个瓶子里分别有 \(x,y\) 升水的状态, 有六种方法可以转移到下一个状态,分别是 FILL(1),FILL(2),DROP(1),DROP(2),POUR(1,2),POUR(2,1)

由于要记录步骤,我们可以用一个字符串表示步骤,上述六个步骤分别对应字符中的 \(1,2,3,4,5,6\)

代码
#include<iostream>
#include<queue>
#include<string>
using namespace std;

struct node{
    int x, y;
    string step;
};
bool fnd;
bool vis[105][105];
const string cmd[] = {"FILL(1)", "FILL(2)", "DROP(1)", "DROP(2)", "POUR(1,2)", "POUR(2,1)"};
int A, B, C;

void bfs()
{
    queue<node> q;
    q.push({0, 0, ""});
    vis[0][0] = true;
    while(!q.empty()) {
        node t = q.front();
        q.pop();
        if(t.x == C || t.y == C) {
            cout << t.step.size() << endl;
            for(int i = 0; i < t.step.size(); i++)
               cout << cmd[t.step[i] - '1'] << endl;
            fnd = true;
            return;
        }
        //FILL(1)
        if(!vis[A][t.y]) {
            q.push({A, t.y, t.step + "1"});
            vis[A][t.y] = true;
        }
        //FILL(2)
        if(!vis[t.x][B]) {
            q.push({t.x, B, t.step + "2"});
            vis[t.x][B] = true;
        }
        if(t.x != 0) {
            if(!vis[0][t.y]) {
                q.push({0, t.y, t.step + "3"});
                vis[0][t.y] = true;
            }
            if(t.x + t.y > B) {
                if(!vis[t.x + t.y - B][B]) {
                    q.push({t.x + t.y - B, B, t.step + "5"});
                    vis[t.x + t.y - B][B] = true;
                }
            }else {
                if(!vis[0][t.x + t.y]) {
                    q.push({0, t.x + t.y, t.step + "5"});
                    vis[0][t.x + t.y] = true;
                }
            }
        }
        if(t.y != 0) {
            if(!vis[t.x][0]) {
                q.push({t.x, 0, t.step + "4"});
                vis[t.x][0] = true;
            }
            if(t.x + t.y > A) {
                if(!vis[A][t.x + t.y - A]) {
                    q.push({A, t.x + t.y - A, t.step + "6"});
                    vis[A][t.x + t.y - A] = true;
                }
            }else {
                if(!vis[t.x + t.y][0]) {
                    q.push({t.x + t.y, 0, t.step + "6"});
                    vis[t.x + t.y][0] = true;
                }
            }
        }   
    }
}

int main()
{
    cin >> A >> B >> C;
    bfs();
    if(!fnd)
        cout << "impossible" << endl;
    return 0;
}

6.POJ 1426 Find The Multiple

题目

POJ 1426

分析

这道题实际上是一道枚举题,用二进制枚举子集的思想,我们可以枚举出只由 \(0\)\(1\) 组成的十进制数,并判断它是否是 \(n\) 的倍数,经过测试, \(1\)\(200\) 之间的所有数的倍数都可以由 \(20\) 位以内的十进制数组成

代码
#include<iostream>
using namespace std;

int main()
{
    int n;
    cin >> n;
    while(n) {
        for(int i = 1; i < (1 << 20); i++) {
            unsigned long long m = 0;
            unsigned long long pw = 1;
            for(int j = 0; j < 20; j++) {
                if((1 << j) & i)
                    m = m + pw;
                pw *= 10;
            }
            if(m % n == 0) {
                cout << m << endl;
                break;
            }
        }
        cin >> n;
    }
    return 0;
}

7.POJ 3279 Fliptile

题目

POJ 3279

分析

这道题与前面一题思路类似

在逐行进行操作的情况下,可以发现:

  • 如果上一行有黑色方块,那么下一行同一列的位置上一定要翻转,否则就无法改变为白色
  • 如果上一行是白色方块,那下一行同一列的位置上一定不能翻转,因为逐行操作,进行翻转了后上一行的状态将无法改变

所以根据上一行能确定下一行,进一步的,只需要第一行的状态,就可以确定整个地图的状态,所以可以枚举第一行的状态

易知两次翻转和不翻转是等价的,所以每个方块翻转的次数只能是 \(0\)\(1\) ,所以第一行翻转的状态可以看作一个二进制数,采用二进制枚举即可

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;

int m, n;
int ans1[20][20];
int ans2[20][20];
bool mat[20][20];
bool mat1[20][20];
int dx[] = {0, 1, 0, -1, 0};
int dy[] = {0, 0, 1, 0, -1};

void flip(int x, int y)
{
    for(int i = 0; i < 5; i++) {
        int nx = x + dx[i], ny = y + dy[i];
        mat1[nx][ny] = !mat1[nx][ny];
    }
}

int main()
{
    cin >> m >> n;
    for(int i = 1; i <= m; i++)
        for(int j = 1; j <= n; j++)
            cin >> mat[i][j];
    int cnt = 0, ans = 1e9;
    for(int i = 0; i < (1 << n); i++) {
        memcpy(mat1, mat, sizeof(mat));
        memset(ans1, 0, sizeof(ans1));
        for(int j = 0; j < n; j++) {
            if((1 << j) & i) {
                flip(1, j + 1);
                ans1[1][j + 1]++;
                cnt++;      
            }
        }
        for(int j = 2; j <= m; j++) {
            for(int k = 1; k <= n; k++) {
                if(mat1[j - 1][k]) {
                    flip(j, k);
                    ans1[j][k]++;
                    cnt++;              
                }
            }
        }
        bool fnd = true;
        for(int j = 1; j <= n; j++) {
            if(mat1[m][j]) {
                fnd = false;
                break;
            }
        }
        if(fnd && cnt < ans) {
            ans = cnt;
            memcpy(ans2, ans1, sizeof(ans1));
        }
    }
    if(ans >= 1e9) {
        cout << "IMPOSSIBLE" << endl;
        return 0;
    }
    for(int i = 1; i <= m; i++) {
        for(int j = 1; j <= n; j++)
            cout << ans2[i][j] << " ";
        cout << endl;
    }
    return 0;
}

7.POJ 1321 棋盘问题

题目

POJ 1321

分析

八皇后问题的变形

代码
#include<cstdio>
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;

struct point{
    int x, y;
};
int n, k, ans = 0;
vector<point> p;
bool visr[10];
bool visc[10]; 

void dfs(int t, int last)
{
    if(t == k + 1) {
        ans++;
        return;
    }
    int siz = p.size();
    for(int i = last; i < siz; i++) {
        if(!visr[p[i].x] && !visc[p[i].y]){
            visr[p[i].x] = visc[p[i].y] = true;
            dfs(t + 1, i + 1);
            visr[p[i].x] = visc[p[i].y] = false;
        }
    }
}

int main()
{
    scanf("%d%d", &n, &k);
    while(n != -1) {
        p.clear();
        ans = 0;
        memset(visr, 0, sizeof(visr));
        memset(visc, 0, sizeof(visc));
        getchar();
        for(int i = 1; i <= n; i++) {
            for(int j = 1; j <= n; j++)
                if(getchar() == '#')
                    p.push_back({i, j});
            getchar();
        }
        dfs(1, 0);
        printf("%d\n", ans);
        scanf("%d%d", &n, &k);
    }
    return 0;
}
posted @ 2022-01-17 20:47  f(k(t))  阅读(12)  评论(0编辑  收藏  举报