[kuangbin带你飞]专题1

这篇博客主要介绍了使用简单搜索算法解决各种问题,如棋盘摆放、三维迷宫、追捕奶牛、翻转瓷砖等,涉及BFS和DFS策略。通过实例讲解了如何应用这些算法找到最优解。
摘要由CSDN通过智能技术生成

专题一 简单搜索

POJ 1321 棋盘问题

在一个给定形状的棋盘(形状可能是不规则的)上面摆放棋子,棋子没有区别。要求摆放时任意的两个棋子不能放在棋盘中的同一行或者同一列,请编程求解对于给定形状和大小的棋盘,摆放k个棋子的所有可行的摆放方案C。

直接DFS,遍历所有的可能。因为是一层一层遍历的,所以只要记录每一列有没有出现过就可以

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 1000 + 10;
int n, m;
char map[maxn][maxn];
bool y[maxn];
int sum = 0, tot = 0;

void dfs(int x)
{
    if (x == n) {
        if (sum == m) {
            tot++;
        }
        return;
    }
    dfs(x + 1);
    for (int i = 0; i < n; i++) {
        if (map[x][i] == '#' && !y[i]) {
            y[i] = true;
            sum++;
            dfs(x + 1);
            y[i] = false;
            sum--;
        }
    }
}

int main()
{
    while (1) {
        tot = 0, sum = 0;
        memset(y, 0, sizeof(y));
        scanf("%d%d", &n, &m);
        if (n == -1)
            return 0;
        for (int i = 0; i < n; i++) {
            scanf("%s", map[i]);
        }
        dfs(0);
        cout << tot << endl;
    }
    return 0;
}

POJ 2251 Dungeon Master

你被困在一个三维地牢,需要找到最快的出路!地牢是由单元立方体组成的,这些立方体可能是石头,也可能不是石头。往北、往南、向东、向西、向上或向下移动一个单元需要一分钟。你不能斜着走,迷宫四周都是坚硬的岩石。
有可能逃跑吗?如果可以,需要多长时间?

升级版走迷宫,从两维变成三维,方向从四个方向变成六个方向,直接BFS找最短的路就好

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 42;
int r, n, m;
char map[maxn][maxn][maxn];
int p[maxn][maxn][maxn], tot[maxn][maxn][maxn];
int x1, y1, z1;
int xx[6] = { 1, -1, 0, 0, 0, 0 };
int yy[6] = { 0, 0, 1, -1, 0, 0 };
int zz[6] = { 0, 0, 0, 0, 1, -1 };
struct Point {
    int x, y, z;
};

int bfs(int x, int y, int z)
{
    memset(p, 0, sizeof(p));
    memset(tot, 0, sizeof(tot));
    tot[z][x][y] = 1;
    queue<Point> q;
    q.push((Point){ x, y, z });
    while (!q.empty()) {
        Point k = q.front();
        q.pop();
        for (int i = 0; i < 6; i++) {
            int x0 = k.x + xx[i];
            int y0 = k.y + yy[i];
            int z0 = k.z + zz[i];
            if (x0 >= 0 && x0 < n && y0 >= 0 && y0 < m && z0 >= 0 && z0 < r && map[z0][x0][y0] != '#' && !tot[z0][x0][y0]) {
                tot[z0][x0][y0] = 1;
                p[z0][x0][y0] = p[k.z][k.x][k.y] + 1;
                q.push((Point){ x0, y0, z0 });
                if (map[z0][x0][y0] == 'E') {
                    return p[z0][x0][y0];
                }
            }
        }
    }
    return -1;
}

int main()
{
    while (1) {
        scanf("%d%d%d", &r, &n, &m);
        if (n == 0 && r == 0 && m == 0)
            return 0;
        for (int k = 0; k < r; k++) {
            for (int i = 0; i < n; i++) {
                scanf("%s", map[k][i]);
                for (int j = 0; j < m; j++) {
                    if (map[k][i][j] == 'S') {
                        x1 = i;
                        y1 = j;
                        z1 = k;
                    }
                }
            }
        }
        int l = bfs(x1, y1, z1);
        if (l == -1)
            printf("Trapped!\n");
        else
            printf("Escaped in %d minute(s).\n", l);
    }
    return 0;
}

POJ 3278 Catch That Cow

农场主约翰已被告知一头逃跑的母牛的位置,他想立即抓住她。它从数轴上的点N开始,牛在同一数轴上的点K开始。农夫约翰有两种交通方式:步行和心灵运输。
*步行:FJ可以在一分钟内从X点移动到X - 1或X + 1点
*心灵移植:FJ可以在一分钟内从任意X点移动到2×X点。
如果这头牛根本不知道自己在追它,不动的话,农夫约翰要多久才能把它找回来?

BFS遍历所有的情况求最优解,不过要考虑FJ可以到达最远的距离防止TL。。。

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

const int maxn = 200000 + 100;
int n, m;
bool vis[maxn];
int p[maxn], t;

int bfs(int x)
{
    queue<int> q;
    vis[x] = true;
    q.push(x);
    while (!q.empty()) {
        int k = q.front();
        q.pop();
        t = k + 1;
        if (t >= 0 && t <= 150000 && !vis[t]) {
            vis[t] = true;
            p[t] = p[k] + 1;
            q.push(t);
            if (t == m)
                return p[t];
        }
        t = k - 1;
        if (t >= 0 && t <= 150000 && !vis[t]) {
            vis[t] = true;
            p[t] = p[k] + 1;
            q.push(t);
            if (t == m)
                return p[t];
        }
        t = k * 2;
        if (t >= 0 && t <= 150000 && !vis[t]) {
            vis[t] = true;
            p[t] = p[k] + 1;
            q.push(t);
            if (t == m)
                return p[t];
        }
    }
    return 0;
}

int main()
{
    cin >> n >> m;
    int l = bfs(n);
    cout << l << endl;
    return 0;
}

POJ 3279 Fliptile

农夫约翰知道一头智力满足的奶牛是一头快乐的奶牛,它会产更多的牛奶。他为奶牛安排了一项脑力活动,让它们操纵一个M×N的网格(1≤M≤15;1≤N≤15)方片,每方片一面为黑色,另一面为白色。
正如人们所猜测的,当一块白色瓷砖翻转时,它会变成黑色;当翻转一个黑色的瓦片时,它会变成白色。当奶牛翻转瓷砖时,它们会得到奖励,这样每块瓷砖都有一面朝上的白色。然而,奶牛的蹄子相当大,当它们试图翻转某个瓦片时,它们也会翻转所有相邻的瓦片(与翻转瓦片共享完整边缘的瓦片)。由于抛硬币很累人,奶牛们想把抛硬币的次数降到最低。
帮助奶牛确定所需翻转的最少次数,以及要翻转到最少的位置。如果有多种方法可以用最少的投掷次数来完成任务,那么当将输出视为字符串时,返回字典排序最少的方法。如果任务不可能完成,打印一行“不可能”。

翻瓷砖问题,每次翻一个之后,前后左右四个方向的瓷砖也会被翻过.
先不考虑第一列,在第二列中为了让第一列全为正,所以应该把第一列为反的瓷砖的下面的进行反转,以此类推,直到最后一列,若最后最后一列全为正则合理,否则不合理,所以只要第一列确定了后面的结果就确定了,所以只要把第一列的所以情况枚举出来就可以。

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

const int maxn = 100;
int map[maxn][maxn], p[maxn], q[maxn][maxn], t[maxn][maxn], lll[maxn][maxn];
int n, m;

void Put(int x, int y)
{
    int xx[5] = { 0, 0, 0, 1, -1 };
    int yy[5] = { 1, -1, 0, 0, 0 };
    for (int i = 0; i < 5; i++) {
        int x0 = x + xx[i];
        int y0 = y + yy[i];
        if (x0 >= 0 && y0 >= 0 && x0 < n && y0 < m) {
            q[x0][y0] ^= 1;
        }
    }
}

int work()
{
    int sum = 0;
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            q[i][j] = map[i][j];
        }
    }
    for (int i = 0; i < m; i++) {
        if (p[i]) {
            Put(0, i);
            sum++;
            t[0][i] = 1;
        }
    }
    for (int i = 1; i < n; i++) {
        for (int j = 0; j < m; j++) {
            if (q[i - 1][j]) {
                Put(i, j);
                sum++;
                t[i][j] = 1;
            }
        }
    }
    for (int i = 0; i < m; i++) {
        if (q[n - 1][i])
            return -1;
    }
    return sum;
}

int main()
{
    scanf("%d%d", &n, &m);
    for (int i = 0; i < n; i++) {
        for (int j = 0; j < m; j++) {
            scanf("%d", &map[i][j]);
        }
    }
    int flag = 0, maxx = 1e9;
    for (int i = 0; i < (1 << m); i++) {
        memset(p, 0, sizeof(p));
        memset(t, 0, sizeof(t));
        for (int j = 0; j < m; j++) {
            if ((1 << j) & i)
                p[j] = 1;
        }
        int l = work();
        if (l != -1) {
            flag = 1;
            if (l < maxx) {
                maxx = l;
                for (int i = 0; i < n; i++) {
                    for (int j = 0; j < m; j++) {
                        lll[i][j] = t[i][j];
                    }
                }
            }
        }
    }
    if (!flag) {
        cout << "IMPOSSIBLE" << endl;
    } else {
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                printf("%d ", lll[i][j]);
            }
            cout << endl;
        }
    }
    return 0;
}

POJ 1426 Find The Multiple

给定一个正整数n,编写一个程序找出一个n的非零倍数m,其十进制表示仅包含数字0和1。您可以假设n不大于200,并且对应的m包含不超过100位小数。

直接暴力枚举所有的0,1情况从1开始每次×10,或者×10+1,得到的数对n取模就可以

#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;
#define ll long long

void bfs(int n)
{
    queue<ll> q;
    q.push(1);
    while (!q.empty()) {
        ll k = q.front();
        if (k % n == 0) {
            cout << k << endl;
            return;
        }
        q.pop();
        q.push(k * 10);
        q.push(k * 10 + 1);
    }
    return;
}

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

POJ 3126 Prime Path

-事实上,我有。你看,有一场编程比赛正在进行……帮助总理在任意两个给定的四位素数之间找到最便宜的素数路径!当然,第一个数字必须是非零的。在上面的例子中有一个解。
1033
1733
3733
3739
3779
8779
8179
这个解决方案的成本是6英镑。注意,第2步中粘贴的数字1不能在最后一步中重用——必须购买一个新的1。

直接暴力枚举每一位的情况,如果是素数就继续遍历直到找到另一个素数。

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

int T, sum;
int aa, bb;
bool vis[10000];
struct node {
    int x, cnt;
};

bool pd(int x)
{
    if (x == 0 || x == 1)
        return false;
    for (int i = 2; i * i <= x; i++) {
        if (x % i == 0)
            return false;
    }
    return true;
}

int bfs(int x, int cnt)
{
    queue<node> q;
    q.push((node){ x, cnt });
    vis[x] = true;
    if (x == bb) {
        return 0;
    }
    while (!q.empty()) {
        node k = q.front();
        q.pop();
        for (int i = 0; i <= 9; i++) {
            int a = k.x % 10;
            int b = (k.x / 10) % 10;
            int c = (k.x / 100) % 10;
            int d = (k.x / 1000) % 10;
            int t1 = (((((i * 10) + c) * 10) + b) * 10) + a;
            if (pd(t1) && !vis[t1] && i != 0) {
                vis[t1] = true;
                q.push((node){ t1, k.cnt + 1 });
                if (t1 == bb) {
                    return k.cnt + 1;
                }
            }
            t1 = (((((d * 10) + i) * 10) + b) * 10) + a;
            if (pd(t1) && !vis[t1]) {
                vis[t1] = true;
                q.push((node){ t1, k.cnt + 1 });
                if (t1 == bb) {
                    return k.cnt + 1;
                }
            }
            t1 = (((((d * 10) + c) * 10) + i) * 10) + a;
            if (pd(t1) && !vis[t1]) {
                vis[t1] = true;
                q.push((node){ t1, k.cnt + 1 });
                if (t1 == bb) {
                    return k.cnt + 1;
                }
            }
            t1 = (((((d * 10) + c) * 10) + b) * 10) + i;
            if (pd(t1) && !vis[t1]) {
                vis[t1] = true;
                q.push((node){ t1, k.cnt + 1 });
                if (t1 == bb) {
                    return k.cnt + 1;
                }
            }
        }
    }
    return -1;
}

int main()
{
    cin >> T;
    while (T--) {
        memset(vis, 0, sizeof(vis));
        scanf("%d%d", &aa, &bb);
        int l = bfs(aa, 0);
        if (l == -1)
            cout << "Impossible" << endl;
        else
            cout << l << endl;
    }
    return 0;
}

POJ 3087 Shuffle’m Up

扑克玩家在牌桌上最常见的消遣就是洗牌。洗牌筹码由两堆扑克筹码开始,分别是S1和S2,每堆都包含C个筹码。每个堆栈可以包含几种不同颜色的芯片。
实际的shuffle操作是将S1的芯片与S2的芯片交叉,如下图所示,C = 5:
avatar
单个结果堆栈S12包含2 * C芯片。S12的底端芯片是S2的底端芯片。在那个芯片的上面,是S1的最下面的芯片。交叉过程继续从S2的底部取出第二个芯片并将其放置在S12上,然后从S1的底部取出第二个芯片,以此类推,直到S1的顶部芯片被放置在S12上。
洗牌操作后,将S12分成两个新的栈,从S12中取出最底层的C片形成一个新的S1,从S12中取出顶层的C片形成一个新的S2。然后可以重复洗牌操作以形成一个新的S12。
对于这个问题,您将编写一个程序来确定一个特定的结果堆栈S12是否可以通过将两个堆栈打乱若干次来形成。

直接遍历所有洗牌后的状态即可

#include <cstdio>
#include <cstring>
#include <iostream>
#include <map>
#include <string>
using namespace std;
const int maxn = 2000 + 100;
int T;
int n, minn, sum;
string a, b, c;
map<string, bool> q;

void dfs()
{
    string t;
    for (int i = 0; i < n; i++) {
        t += b[i];
        t += a[i];
    }
    if (t == c) {
        minn = min(sum, minn);
        return;
    }
    if (!q[t]) {
        q[t] = true;
        a = "";
        b = "";
        for (int i = 0; i < n; i++) {
            a += t[i];
        }
        for (int i = 0; i < n; i++) {
            b += t[i + n];
        }
        sum++;
        dfs();
    }
}

int main()
{
    cin >> T;
    for (int lll = 1; lll <= T; lll++) {
        minn = 1e9;
        cin >> n;
        cin >> a >> b >> c;
        q.clear();
        sum = 1;
        dfs();
        if (minn == 1e9)
            printf("%d -1\n", lll);
        else
            printf("%d %d\n", lll, minn);
    }
    return 0;
}

POJ 3414 Pots

你有两个壶,分别有A和B升的体积。可以执行以下操作:

  1. FILL(i) 从水龙头往i(1≤i≤2)罐内灌满;
  2. DROP(i) 把锅里的水倒进排水沟里;
  3. POUR(i,j) 从锅i倒到锅j;在这个操作之后,要么锅j满了(可能锅i中还剩下一些水),要么锅i是空的(它的所有内容都被移到了锅j中)。

编写一个程序,找出这些操作中最短的可能顺序,使其中一个罐子刚好能产生C公升的水。

BFS 枚举每种变化,记录最短的路径,其实不是很难就是写起来比较复杂而已

#include <cstdio>
#include <iostream>
#include <queue>
using namespace std;

int a, b, c, xx, yy;
struct E {
    int x, y, cnt;
};
struct p {
    int x, y, o;
} e[1000][1000];
bool vis[1000][1000];
int bfs()
{
    queue<E> q;
    q.push((E){ 0, 0, 0 });
    vis[0][0] = true;
    while (!q.empty()) {
        E k = q.front();
        if (k.x == c || k.y == c) {
            xx = k.x, yy = k.y;
            return k.cnt;
        }

        q.pop();
        int x, y;
        x = 0;
        y = k.y;
        if (!vis[x][y]) {
            e[x][y] = (p){ k.x, k.y, 1 };
            vis[x][y] = true;
            q.push((E){ x, y, k.cnt + 1 });
        }
        x = k.x;
        y = 0;
        if (!vis[x][y]) {
            e[x][y] = (p){ k.x, k.y, 2 };
            vis[x][y] = true;
            q.push((E){ x, y, k.cnt + 1 });
        }
        x = a;
        y = k.y;
        if (!vis[x][y]) {
            e[x][y] = (p){ k.x, k.y, 3 };
            vis[x][y] = true;
            q.push((E){ x, y, k.cnt + 1 });
        }
        x = k.x;
        y = b;
        if (!vis[x][y]) {
            e[x][y] = (p){ k.x, k.y, 4 };
            vis[x][y] = true;
            q.push((E){ x, y, k.cnt + 1 });
        }
        if (k.x > b - k.y) {
            x = k.x - (b - k.y);
            y = b;
        } else {
            x = 0;
            y = k.y + k.x;
        }
        if (!vis[x][y]) {
            e[x][y] = (p){ k.x, k.y, 5 };
            vis[x][y] = true;
            q.push((E){ x, y, k.cnt + 1 });
        }
        if (k.y > a - k.x) {
            x = a;
            y = k.y - (a - k.x);
        } else {
            x = k.y + k.x;
            y = 0;
        }
        if (!vis[x][y]) {
            e[x][y] = (p){ k.x, k.y, 6 };
            vis[x][y] = true;
            q.push((E){ x, y, k.cnt + 1 });
        }
    }
    return -1;
}

void print(int x, int y)
{
    if (x == 0 && y == 0)
        return;
    print(e[x][y].x, e[x][y].y);
    switch (e[x][y].o) {
    case 1:
        cout << "DROP(1)";
        break;
    case 2:
        cout << "DROP(2)";
        break;
    case 3:
        cout << "FILL(1)";
        break;
    case 4:
        cout << "FILL(2)";
        break;
    case 5:
        cout << "POUR(1,2)";
        break;
    case 6:
        cout << "POUR(2,1)";
        break;
    default:
        break;
    }
    cout << endl;
}

int main()
{
    cin >> a >> b >> c;
    int l = bfs();
    if (l == -1)
        cout << "impossible" << endl;
    else {
        cout << l << endl;
        print(xx, yy);
    }
}

FZU 2150 Fire Game

胖哥和迷宫在一个N*M的棋盘(N行,M列)上玩一种特殊的(hentai)游戏。开始的时候,每个格子都是草或者是空的然后他们开始点燃所有的草。首先,他们选择由草和火组成的两个网格。我们都知道,火可以在草地上蔓延。如果网格(x, y)在t时刻触发,则与此网格相邻的网格将在t+1时刻触发,t+1表示网格(x+1, y) (x, y+1) (x, y+1) (x, y+1) (x, y) (x, y+1) (x, y) (x, y) (x, y)这个过程结束时,没有新的网格得到火。如果所有由草组成的格子都被烧坏了,胖哥和迷宫就会站在格子中间,玩一个更特别的(hentai)游戏。(也许是在最后一个问题中解密的OOXX游戏,谁知道呢。)
你可以假设木板上的草永远不会烧完,空格子永远不会着火。
注意,他们选择的两个网格可以是相同的。

点火问题,选择两个点开始BFS,求最短的时间。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 200 + 10;
int T, n, m;
char map[maxn][maxn];
int vis[maxn][maxn];
int xx[4] = { 1, -1, 0, 0 };
int yy[4] = { 0, 0, 1, -1 };
int x1, x2, y1, y2;
struct Point {
    int x, y;
} e[maxn];
int tot = 0, cnt;
int dfs(int a, int b)
{
    cnt = 1;
    queue<Point> q;
    q.push((Point){ e[a].x, e[a].y });
    q.push((Point){ e[b].x, e[b].y });
    vis[e[a].x][e[a].y] = 1;
    vis[e[b].x][e[b].y] = 1;
    while (!q.empty()) {
        Point k = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int x0 = k.x + xx[i];
            int y0 = k.y + yy[i];
            if (x0 >= 0 && x0 < n && y0 >= 0 && y0 < m && map[x0][y0] == '#' && !vis[x0][y0]) {
                vis[x0][y0] = vis[k.x][k.y] + 1;
                q.push((Point){ x0, y0 });
                cnt = max(cnt, vis[x0][y0]);
            }
        }
    }
    return cnt;
}
int main()
{
    cin >> T;
    for (int ll = 1; ll <= T; ll++) {
        tot = 0;
        scanf("%d%d", &n, &m);
        for (int i = 0; i < n; i++) {
            scanf("%s", map[i]);
            for (int j = 0; j < m; j++) {
                if (map[i][j] == '#') {
                    e[++tot].x = i;
                    e[tot].y = j;
                }
            }
        }
        int sum = 1e9;
        for (int i = 1; i <= tot; i++) {
            for (int j = i; j <= tot; j++) {
                memset(vis, 0, sizeof(vis));
                int lll = dfs(i, j);
                int flag = 0;
                for (int k = 0; k < n; k++) {
                    for (int l = 0; l < m; l++) {
                        if (map[k][l] == '#' && !vis[k][l]) {
                            flag = 1;
                            break;
                        }
                        if (flag)
                            break;
                    }
                }
                if (!flag)
                    sum = min(sum, lll - 1);
            }
        }
        if (sum == 1e9)
            printf("Case %d: -1\n", ll);
        else
            printf("Case %d: %d\n", ll, sum);
    }
    return 0;
}

UVA 11624 Fire!

乔在迷宫里工作。不幸的是,迷宫的部分区域有着火了,迷宫的主人忘记生火了逃跑计划。帮助乔逃离迷宫。给定乔在迷宫中的位置和迷宫的哪个方块是着火了,你必须先确定乔是否能逃出迷宫火到达了他的身边,他能以多快的速度做到这一点。乔和火每分钟移动一个正方形,垂直或水平(不是对角)。大火向四面八方蔓延从每一个着火的广场。乔可以从任何一个迷宫中出迷宫边缘的正方形。既不是乔也不是火可以进入一个被墙占据的广场。

两次遍历,第一次求出火蔓延到各个点的时间,第二次在着火的约束下求走出迷宫的最短时间。注意着火点不止有有一个。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 1000 + 10;
int n, m, T, ttt;
int x1, y1, x2[maxn], y2[maxn], vis[maxn][maxn];
char map[maxn][maxn];
int xx[4] = { 1, -1, 0, 0 };
int yy[4] = { 0, 0, 1, -1 };
struct Point {
    int x, y;
};
bool tot[maxn][maxn];
void bfs()
{

    memset(tot, 0, sizeof(tot));
    queue<Point> q;
    for (int i = 1; i <= ttt; i++) {
        q.push((Point){ x2[i], y2[i] });
        tot[x2[i]][y2[i]] = 1;
        vis[x2[i]][y2[i]] = 1;
    }
    while (!q.empty()) {
        Point k = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int x0 = k.x + xx[i];
            int y0 = k.y + yy[i];
            if (x0 >= 0 && x0 < n && y0 >= 0 && y0 < m && map[x0][y0] != '#' && !tot[x0][y0]) {
                tot[x0][y0] = 1;
                vis[x0][y0] = vis[k.x][k.y] + 1;
                q.push((Point){ x0, y0 });
            }
        }
    }
}
int p[maxn][maxn];
int bfs2(int x, int y)
{

    memset(p, 0, sizeof(p));
    tot[x][y] = 1;
    memset(tot, 0, sizeof(tot));
    p[x][y] = 1;
    queue<Point> q;
    q.push((Point){ x, y });
    if (x == 0 || y == 0 || x == n - 1 || y == m - 1)
        return 1;
    while (!q.empty()) {
        Point k = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int x0 = k.x + xx[i];
            int y0 = k.y + yy[i];
            if (x0 >= 0 && x0 < n && y0 >= 0 && y0 < m && map[x0][y0] != '#' && !tot[x0][y0] && (p[k.x][k.y] + 1 < vis[x0][y0] || !vis[x0][y0])) {
                tot[x0][y0] = 1;
                p[x0][y0] = p[k.x][k.y] + 1;
                q.push((Point){ x0, y0 });
                if (x0 == 0 || y0 == 0 || x0 == n - 1 || y0 == m - 1)
                    return p[x0][y0];
            }
        }
    }
    return -1;
}

int main()
{
    scanf("%d", &T);
    while (T--) {
        memset(vis, 0, sizeof(vis));
        scanf("%d%d", &n, &m);
        ttt = 0;
        for (int i = 0; i < n; i++) {
            scanf("%s", map[i]);
            for (int j = 0; j < m; j++) {
                if (map[i][j] == 'J') {
                    x1 = i, y1 = j;
                }
                if (map[i][j] == 'F') {
                    x2[++ttt] = i, y2[ttt] = j;
                }
            }
        }
        bfs();
        int t = bfs2(x1, y1);
        if (t == -1)
            cout << "IMPOSSIBLE" << endl;
        else
            cout << t << endl;
    }
    return 0;
}

POJ 3984 迷宫问题

定义一个二维数组:
int maze[5][5] = {
0, 1, 0, 0, 0,
0, 1, 0, 1, 0,
0, 0, 0, 0, 0,
0, 1, 1, 1, 0,
0, 0, 0, 1, 0,
};
它表示一个迷宫,其中的1表示墙壁,0表示可以走的路,只能横着走或竖着走,不能斜着走,要求编程序找出从左上角到右下角的最短路线。

BFS求最短路,并记录最短路的路径

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 10;
int map[maxn][maxn], tot = 1;
int vis[maxn][maxn], p[maxn][maxn], x[maxn], y[maxn];
int xx[4] = { 1, 0, 0, -1 };
int yy[4] = { 0, -1, 1, 0 };
struct Point {
    int x, y;
};
void bfs(int a, int b)
{
    queue<Point> q;
    q.push((Point){ a, b });
    while (!q.empty()) {
        Point k = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int x0 = k.x + xx[i];
            int y0 = k.y + yy[i];
            if (x0 >= 0 && x0 < 5 && y0 >= 0 && y0 < 5 && map[x0][y0] == 0 && !vis[x0][y0]) {
                vis[x0][y0] = 1;
                p[x0][y0] = 3 - i;
                q.push((Point){ x0, y0 });
                if (x0 == 4 && y0 == 4)
                    return;
            }
        }
    }
}
void dfs(int ax, int ay)
{
    if (ax == 0 && ay == 0)
        return;
    x[++tot] = ax + xx[p[ax][ay]];
    y[tot] = ay + yy[p[ax][ay]];

    dfs(x[tot], y[tot]);
}
int main()
{
    for (int i = 0; i < 5; i++) {
        for (int j = 0; j < 5; j++) {
            scanf("%d", &map[i][j]);
        }
    }
    bfs(0, 0);
    dfs(4, 4);
    x[1] = 4, y[1] = 4;
    for (int i = tot; i > 0; i--) {
        printf("(%d, %d)\n", x[i], y[i]);
    }
    return 0;
}

HDU 1241 Oil Deposits

地质urvcomp地质勘测公司负责探测地下油层。GeoSurvComp每次处理一个大的矩形区域,并创建一个网格,将土地划分为许多方形地块。然后用传感设备分别分析每个地块,以确定该地块是否含有石油。一块含有石油的土地叫做口袋。如果两个储层相邻,则它们属于同一油层的一部分。石油储量可以相当大,可能包含许多口袋。你的工作是确定有多少不同的石油蕴藏在一个网格。

寻找地图中有多少联通块,直接遍历把遍历过的点改为*即可。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
using namespace std;
const int maxn = 100 + 10;
int n, m, sum;
char map[maxn][maxn];
bool vis[maxn][maxn];
int xx[8] = { 1, -1, 0, 0, 1, 1, -1, -1 };
int yy[8] = { 0, 0, 1, -1, 1, -1, 1, -1 };
void dfs(int x, int y)
{
    map[x][y] = '*';
    for (int i = 0; i < 8; i++) {
        int x0 = x + xx[i];
        int y0 = y + yy[i];
        if (x >= 0 && x < n && y >= 0 && y < m && map[x0][y0] == '@') {
            dfs(x0, y0);
        }
    }
}
int main()
{
    while (1) {
        memset(vis, 0, sizeof(vis));
        scanf("%d%d", &n, &m);
        sum = 0;
        if (n == 0)
            return 0;
        for (int i = 0; i < n; i++) {
            scanf("%s", map[i]);
        }
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (map[i][j] == '@') {
                    dfs(i, j);
                    sum++;
                }
            }
        }
        printf("%d\n", sum);
    }
    return 0;
}

HDU 1495 非常可乐

大家一定觉的运动以后喝可乐是一件很惬意的事情,但是seeyou却不这么认为。因为每次当seeyou买了可乐以后,阿牛就要求和seeyou一起分享这一瓶可乐,而且一定要喝的和seeyou一样多。但seeyou的手中只有两个杯子,它们的容量分别是N 毫升和M 毫升 可乐的体积为S (S<101)毫升 (正好装满一瓶) ,它们三个之间可以相互倒可乐 (都是没有刻度的,且 S==N+M,101>S>0,N>0,M>0) 。聪明的ACMER你们说他们能平分吗?如果能请输出倒可乐的最少的次数,如果不能输出"NO"。

简单的模拟暴力,但在条件处理是要注意,容易出现错误

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

const int maxn = 200;
int b[3];
bool vis[maxn][maxn][maxn];

struct E {
    int val[3], cnt;
};

bool pd(int tt[3])
{
    int t = b[0] / 2;
    if ((tt[0] == t && tt[1] == t) || (tt[2] == t && tt[1] == t) || (tt[0] == t && tt[2] == t))
        return true;
    return false;
}

int bfs()
{
    E aa;
    aa.val[0] = b[0], aa.val[1] = 0, aa.val[2] = 0;
    aa.cnt = 0;
    queue<E> q;
    q.push(aa);
    memset(vis, 0, sizeof(vis));
    vis[b[0]][0][0] = true;
    while (!q.empty()) {
        E k = q.front();
        q.pop();
        for (int i = 0; i < 3; i++) {
            if (k.val[i])
                for (int j = 0; j < 3; j++) {
                    if (i == j)
                        continue;
                    E pp = k;
                    pp.cnt++;
                    if (k.val[i] > b[j] - k.val[j]) {
                        pp.val[i] -= b[j] - k.val[j];
                        pp.val[j] = b[j];
                    } else {
                        pp.val[i] = 0;
                        pp.val[j] += k.val[i];
                    }
                    if (!vis[pp.val[0]][pp.val[1]][pp.val[2]]) {
                        vis[pp.val[0]][pp.val[1]][pp.val[2]] = true;
                        q.push(pp);
                        if (pd(pp.val)) {
                            return pp.cnt;
                        }
                    }
                }
        }
    }
    return -1;
}

int main()
{
    while (scanf("%d%d%d", &b[0], &b[1], &b[2]), b[1] + b[0] + b[2]) {
        if (b[0] % 2 == 1)
            printf("NO\n");
        else {
            int l = bfs();
            if (l == -1)
                printf("NO\n");
            else
                printf("%d\n", l);
        }
    }
    return 0;
}

HDU 2612 Find a way

ass在杭州学习了一年,终于抵达家乡宁波。离开宁波一年,依芬飞有很多人要见面。尤其是好朋友梅尔斯基。
一芬飞的家在乡下,但奔驰的家在市中心。于是易芬飞和奔驰约好在肯德基见面。宁波有很多肯德基,他们想选择一个让总时间最小的。
现在给你一张宁波地图,一芬飞和奔驰都可以上下左右移动到相邻的路上,只需要11分钟。

两次BFS,求出每个人到每个KFC的最短距离。

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

const int maxn = 200 + 10;
int n, m, sum;
char map[maxn][maxn];
int vis[maxn][maxn], vis2[maxn][maxn];
int xx[4] = { 1, -1, 0, 0 };
int yy[4] = { 0, 0, 1, -1 };
int x1, x2, y1, y2;
struct Point {
    int x, y;
};

void bfs(int x, int y, int t)
{
    bool tot[maxn][maxn];
    int ttt = 0;
    memset(tot, 0, sizeof(tot));
    tot[x][y] = 1;
    queue<Point> q;
    q.push((Point){ x, y });
    while (!q.empty()) {
        Point k = q.front();
        q.pop();
        for (int i = 0; i < 4; i++) {
            int x0 = k.x + xx[i];
            int y0 = k.y + yy[i];
            if (x0 < 0 || x0 >= n || y0 < 0 || y0 >= m || map[x0][y0] == '#' || tot[x0][y0])
                continue;
            if (t)
                vis[x0][y0] = vis[k.x][k.y] + 1;
            else
                vis2[x0][y0] = vis2[k.x][k.y] + 1;
            tot[x0][y0] = 1;
            q.push((Point){ x0, y0 });
        }
    }
}

int main()
{
    while (~scanf("%d%d", &n, &m)) {
        sum = 0;
        memset(vis, 0, sizeof(vis));
        memset(vis2, 0, sizeof(vis2));
        for (int i = 0; i < n; i++) {
            scanf("%s", map[i]);
            for (int j = 0; j < m; j++) {
                if (map[i][j] == 'Y')
                    x1 = i, y1 = j;
                if (map[i][j] == 'M')
                    x2 = i, y2 = j;
                if (map[i][j] == '@')
                    sum++;
            }
        }
        bfs(x1, y1, 0);
        bfs(x2, y2, 1);
        int minn = 1e9;
        for (int i = 0; i < n; i++) {
            for (int j = 0; j < m; j++) {
                if (map[i][j] == '@' && vis[i][j] && vis2[i][j]) {
                    minn = min(minn, (vis[i][j] + vis2[i][j]) * 11);
                }
            }
        }
        printf("%d\n", minn);
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值