kuangbing带你飞 专题一 简单搜索题解

kuangbin带你飞 专题一 简单搜索

1. POJ 1321 棋盘问题

http://poj.org/problem?id=1321

题目类似八皇后 但是加入了障碍物 以及规则为不能同列和同行

我一开始的思路走入了误区 用bool二维数组判断标记

其实我们可以简化问题

将问题看做 在每一行选一个点落下棋子 而保证棋子不在一列 即可

由此确定出dfs的递归参数 一个是行 一个是棋子数 当超出行的时候回溯 在棋子数满足答案时res++并回溯

有一个值得注意的地方 在一行里 可以选择 也可以选择不放 要注意写出在本行不放的递归函数

char g[10][10];
bool st[10];//这是列的标记 行不需要 因为我们是行行递推下去的
int res, n, k;
 
void dfs(int dep, int num)//第几行 落子数
{
    if(num == k)  {res ++; return;}

    if(dep > n)    return;//越界

    for(int j = 1; j <= n; j++)
    {
        if(g[dep][j] == '#' && !st[j])
        {
            st[j] = true;
            dfs(dep + 1, num + 1);//落子成功
            st[j] = false;
        }
    }
    dfs(dep + 1, num);//在本行没有选到位置落子
}

int main()
{
    while(scanf("%d%d", &n, &k) == 2)
    {
        if(n == -1 && k == -1)  break;
        res = 0;
        for(int i = 1; i <= n; i++)
            scanf("%s", g[i] + 1);

        ms(st, false);

        dfs(1, 0);//从第一行开始 初始落子数0

        printf("%d\n", res);
    }
    return 0;
}

2. POJ 2251 Dungeon Master

http://poj.org/problem?id=2251

这是一个三维地图 读入非常坑爹

反正我觉得题目不能很好地描述题意

思路就是简单的bfs 不过可以向上向下走

调了一下午bug 直接上代码吧

char g[35][35][35];
bool st[35][35][35];
int l, r, c;

int d[6][3]={{1,0,0},{-1,0,0},{0,1,0},{0,-1,0},{0,0,1},{0,0,-1}};//方向向量

struct point
{
    int x, y, z;
    int step;
};

bool check(int x, int y, int z)//判断合法
{
    if(x < 0 || y < 0 || z < 0 || x >= l || y >= r || z >= c ) return true;
    else if(st[x][y][z] == true) return true;
    else if(g[x][y][z] == '#')   return true;
    return false;
}

void bfs(int aa, int bb, int cc)
{
    queue<point> q;
    
    point start;
    start.x = aa, start.y = bb, start.z = cc, start.step = 0;
    q.push(start);
    st[aa][bb][cc] = true;

    while(q.size())
    {
        point t = q.front();
        q.pop();
        
        if(g[t.x][t.y][t.z] == 'E')//达到出口
        {
            cout<<"Escaped in "<< t.step <<" minute(s)."<<endl;
            return;
        }

        for(int i = 0; i < 6; i++)
        {
            int xx = t.x + d[i][0], yy = t.y + d[i][1], zz = t.z + d[i][2];
            if(check(xx, yy, zz))  continue;

            st[xx][yy][zz] = true;
            point tt;
            tt.x = xx, tt.y = yy, tt.z = zz;
            tt.step = t.step + 1;
            q.push(tt);
        }
    }
    cout << "Trapped!" << endl;
    return;
}

int main()
{
    while(cin >> l >> r >> c)
    {
        if(l == 0 && r == 0 && c == 0)  break;
        
        int aa, bb, cc;
        for(int i = 0; i < l; i++)
            for(int j = 0; j < r; j++)
            {
                cin >> g[i][j];//字符串读入
                for(int h = 0; h < c; h++)
                    if(g[i][j][h] == 'S')     aa = i, bb = j, cc = h;//起点
            }
        ms(st, false);
        bfs(aa, bb, cc);
    }
    return 0;
}

3. POJ 3278 Catch That Cow

http://poj.org/problem?id=3278

给出一个起点x 给出一个终点 牛有三种移动方式 前进一格 后退一格 前进x * 2

用BFS的思想可以实现

有一个小点要注意 当n和k的差距很大的时候 需要乘很多次再去回头

所以是有可能有数据会卡在k的很后面的 下面判断合法的时候要注意一下

const int N = 100010;
int dis[N];
bool st[N];

struct node
{
    int x, step;
};

queue<node> q;

void bfs(int x)
{
    node h;
    h.x = x, h.step = 0;
    ms(st, false);

    q.push(h);
    st[h.x] = true;
    
    while(q.size())
    {
        node t = q.front();
        q.pop();

        if(t.x == k)   
        {
            cout << t.step << endl;
            return;
        } 

        for(int i = 0; i < 3; i++)//枚举三种移动方式
        {
            int xx;
            if(i == 0)  xx = t.x + 1;
            else if(i == 1) xx = t.x - 1;
            else if(i == 2) xx = t.x * 2;

            if(xx >= 0 && xx <= N && !st[xx])//之前错的是 xx <= k + 10 考虑不周
            {
                node next;
                next.x = xx, next.step = t.step + 1;
                q.push(next);
                st[xx] = true;
            }
        }
    }
}

int main()
{
    cin >> n >> k;
    bfs(n);
    return 0;
}

4. POJ 3279 Fliptile

http://poj.org/problem?id=3279

是蓝书中的改编版 同样的二进制枚举翻转牌子 但是这题要我们输出的是翻转过的位置 并且还是按字符串的字典序

二维矩阵压缩到字符串的话 这跟二进制枚举字典序其实是一样的 因为二进制枚举是先枚举第一个点翻转 然后

用ans数组记录下翻转的位置 当第一次翻转成功就break直接输出ans数组

如果没有一次翻转成功的话 IMPOSSIBLE

每次枚举的时候要记得清空ans数组 要用一个数组复制原数组翻转 是为了回溯

int m, n, flag;//地图 标记
int g[20][20], b[20][20], ans[20][20];
int d[5][2] = {{0, 0}, {1, 0}, {-1, 0}, {0, 1}, {0, -1}};

void turn(int x, int y)//把0 变成1 把1 变成0
{

    for(int i = 0; i < 5; i++)
    {
        int xx = x + d[i][0], yy = y + d[i][1];z	
        if(~xx && ~yy && xx < n && yy < m) b[xx][yy] ^= 1;
    }
}

bool check()//通过最后一行判断是否翻转完成
{
    for(int i = 0; i < m; i++)
        if(b[n - 1][i] == 1)   return false;
    return true;
}

int main()
{
    cin >> n >> m;
    for(int i = 0; i < n; i++) 
        for(int j = 0; j < m; j++)
            cin >> g[i][j];
    
    for(int i = 0; i < (1 << m); i++)//二进制枚举
    {
        memcpy(b, g, sizeof g);
        ms(ans, 0);

        for(int j = 0; j < m; j++)  
            if( i >> j & 1) turn(0, j), ans[0][j] = 1;//枚举第一行
        
        for(int j = 0; j < n - 1; j++)
            for(int k = 0; k < m; k++)  if(b[j][k]) turn(j + 1, k), ans[j + 1][k] = 1;
        
        if(check())
        {
            flag = 1;
            break;
        }
    }

    if(flag)
    {
        for(int i = 0; i < n; i++)
        {
            for(int j = 0; j < m; j++)
                cout << ans[i][j] << " ";
            cout << endl;
        }
    }
    else    cout << "IMPOSSIBLE" << endl;

    return 0;
}

5. POJ 1426 Find The Multiple

http://poj.org/problem?id=1426

翻译一下这个让人看不懂的题干

给定正整数n 请找出任意一个由0和1组成的是n的倍数的数 答案最大不超过200位数

看起来很吓人 其实不会超出LL的范围 那我们直接枚举这个二进制数 判断是否为n的倍数即可

LL ans[1000000];

int main()
{
    int n;
    while(cin >> n)
    {
        if(n == 0)  break;
        if(n == 1)  {cout << "1" << endl;continue;}
        ans[1] = 1;
        for(int i = 2; i; i++)
        {
            ans[i] = ans[i / 2] * 10 + i % 2;//末位交替出现0和1
            //依次枚举 0, 1, 10, 10+1, 100, 100 + 1, 100 + 10, 100 + 10 + 1 ...
            if(ans[i] % n == 0)
            {
                cout << ans[i] << endl;
                break;
            }
        }
    }
    return 0;
}

6. POJ 3126 Prime Path

http://poj.org/problem?id=3126

题目叫素数路径 题干看的不是很明白 但是样例给的提示比较明显

题意如下 每次改动a中的任意一位 使其变成素数 以此类推 请问最短需要多少次改动才能变成 b

对4个位数进行枚举 + BFS即可

int a, b;
bool is_prime[10010];
bool st[10010];

struct node
{
    int x, step;   
};

void get_prime()
{
    ms(is_prime, true);

    for(int i = 2; i <= 10005; i++)
    {
        if(is_prime[i]) 
            for(int j = i + i; j <= 10005; j += i)
                is_prime[j] = false;
    }
}

int solve()
{
    cin >> a >> b;
    
    ms(st, false);

    queue<node> q;
    node s;
    s.x = a, s.step = 0;
    q.push(s);
    st[s.x] = true;

    while(q.size())
    {
        node t = q.front();
        q.pop();

        if(t.x == b)    return t.step;//终止条件

        for(int i = 1; i <= 9; i++)
        {
            int u = t.x / 10;//枚举个位
            u = u * 10 + i;
            if(is_prime[u] && !st[u])
            {
                st[u] = true;
                q.push({u, t.step + 1});
            }
        }

        for(int i = 0; i <= 9; i++)
        {
            int u = t.x / 100, v = t.x % 10;//枚举十位
            u = u * 100 + v + i * 10;
            if(is_prime[u] && !st[u])
            {
                st[u] = true;
                q.push({u, t.step + 1});
            }
        }

        for(int i = 0; i <= 9; i++)
        {
            int u = t.x / 1000, v = t.x % 100;//枚举百位
            u = u * 1000 + v + i * 100;
            if(is_prime[u] && !st[u])
            {
                st[u] = true;
                q.push({u, t.step + 1});
            }
        }

        for(int i = 1; i <= 9; i++)//不能有前导0
        {
            int u = t.x % 1000;//枚举千位
            u =  i * 1000 + u;
            if(is_prime[u] && !st[u])
            {
                st[u] = true;
                q.push({u, t.step + 1});
            }
        }
    }
    return -1;
}

int main()
{
    int _;
    cin >> _;
    get_prime();

    while(_--)
    {
        int res = solve();
        if(res == -1)   cout << "Impossible" << endl;//不能走到
        else cout << res << endl;
    }
    return 0;
}

7. POJ 3087 Shuffle’m Up

http://poj.org/problem?id=3087

这是一个字符串模拟题 不知道为什么放在搜索专题了

按题意模拟字符串排序即可 终止条件是排序结果重复 此时无解

int cnt, n;
string s1, s2, s12;

void solve()
{
    cin >> n;
    cin >> s1 >> s2 >> s12;
    
    map<string, bool> mp;
    mp[s12] = true;
    int res = 0;
    while(1)
    {
        int num = 0;
        string ss;//初始化
        for(int i = 0; i < n; i++)    ss += s2[i], ss += s1[i];//洗牌过程模拟
        
        res ++;//经过res次洗牌
        if(ss == s12)//有解
        {
            cout << cnt << " " << res << endl;
            return;
        }

        else if(mp[ss])//当经过了一次循环之后 无解
        {
            cout << cnt << " -1" << endl;
            return;
        }

        for(int i = 0; i < n; i++)  s1[i] = ss[i], s2[i] = ss[i + n];
        mp[ss] = true;
    }
}

int main()
{
    int _;
    cin >> _;
    while(_--)
    {
        cnt ++;
        solve();
    }
    return 0;
}

8. POJ 3414 Pots

http://poj.org/problem?id=3414

这题我以为是思维题 找了10分钟规律没有进展 于是看题解 原来他其实就是一道广搜题

6个操作 相当于六个方向的尝试

值得一提的是这道题需要存储路径

我们可以回忆一下 在入门学BFS的时候 有过那种记录下最短路径走过的坐标的题目

其实这道题亦是如此

我们在结构体中加入一个字符数组 就是用于存储路径的 该点成立的时候将字符串用strcpy复制给他

因为ans走过的点都是正确的点 所以他存储的就是正确的路径

int st[110][110];//两个瓶子是否已经经历过了
int a, b, c, flag;

struct node 
{
    int x, y, step;//x y 两个瓶子
    char move[110][10];//记录路径
};

queue<node> q;

node bfs()
{
    node tmp, start;
    start.x = 0, start.y = 0, start.step = 0;
    st[start.x][start.y] = 1;
    q.push(start);

    while(q.size())
    {
        node t = q.front();
        q.pop();

        if(t.x == c || t.y == c)
        {
            flag = 1;
            return t;
        }

        for(int i = 0; i < 6; i++)
        {
            if(i == 0)//fill x
            {
                tmp = t;
                tmp.x = a;
                if(!st[tmp.x][tmp.y])
                {
                    st[tmp.x][tmp.y] = 1;
                    strcpy(tmp.move[tmp.step], "FILL(1)");
                    tmp.step ++;
                    q.push(tmp);
                }
            }

            if(i == 1) //fill y
            {
                tmp = t;
                tmp.y = b;
                if(!st[tmp.x][tmp.y])
                {
                    st[tmp.x][tmp.y] = 1;
                    strcpy(tmp.move[tmp.step], "FILL(2)");
                    tmp.step ++;
                    q.push(tmp);
                }
            }

            if(i == 2)  //drop 1
            {
                tmp = t;
                tmp.x = 0;
                if(!st[tmp.x][tmp.y])
                {
                    st[tmp.x][tmp.y] = 1;
                    strcpy(tmp.move[tmp.step], "DROP(1)");
                    tmp.step ++;
                    q.push(tmp);
                }
            }

            if(i == 3)//drop 2
            {
                tmp = t;
                tmp.y = 0;
                if(!st[tmp.x][tmp.y])
                {
                    st[tmp.x][tmp.y] = 1;
                    strcpy(tmp.move[tmp.step], "DROP(2)");
                    tmp.step ++;
                    q.push(tmp);
                }
            }   

            if(i == 4) //pour(2,1)
            {
                tmp = t;
                if(tmp.y >= (a - tmp.x) )
                {
                    tmp.y -= (a - tmp.x);
                    tmp.x = a;
                }
                else 
                {   
                    tmp.x += tmp.y;
                    tmp.y = 0;
                }

                if(!st[tmp.x][tmp.y])
                {
                    st[tmp.x][tmp.y] = 1;
                    strcpy(tmp.move[tmp.step], "POUR(2,1)");
                    tmp.step ++;
                    q.push(tmp);
                }
            }

            if(i == 5)//pour(1,2)
            {
                tmp = t;
                if(tmp.x >= (b - tmp.y))
                {
                    tmp.x -= (b - tmp.y);
                    tmp.y = b;
                }
                else 
                {
                    tmp.y += tmp.x;
                    tmp.x = 0;
                }

                if(!st[tmp.x][tmp.y])
                {
                    st[tmp.x][tmp.y] = 1;
                    strcpy(tmp.move[tmp.step], "POUR(1,2)");
                    tmp.step ++;
                    q.push(tmp);
                }
            }
        }
    }
}


int main()
{
    while(~scanf("%d%d%d", &a, &b, &c))
    {
        node ans;

        ms(st, false);
        flag = 0;
        ans = bfs();
        if(flag)
        {
            printf("%d\n", ans.step);
            for(int i = 0; i < ans.step; i++)   printf("%s\n", ans.move[i]);
            //每个结构体走的路不一样 所以路径不一样 但是ans是答案的路径 所以一定是正确的
        }
        else printf("impossible\n");
    }
    return 0;
}

9. FZU 2150 Fire Game

题意 可以选择两个草堆(或者同一个) 草堆的火会蔓延 每次1s 如果选择两个草堆点燃可以使所有的草堆被点燃 输出所有草堆被点燃的时间 否则-1

从实现上看 对草堆进行BFS搜索 然后计数 若超过了2次 那就证明选择两个草堆不够 输出-1 反之输出最短时间

但是在实现最短时间这里出现了困难

正解是枚举每两个点 得到最小步数值

int n, m, ans, len;
char g[15][15];
bool vis[15][15];

int d[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

struct node
{
    int x, y, step;
}a[105];//存储起点 用于枚举

queue<node> q;

void clear_queue(queue<node> &q)
{
    queue<node> empty;
    swap(empty, q);
}

bool check(int x, int y)
{
    if(x < 1 || y < 1 || x > n || y > m)    return true;
    if(g[x][y] == '.')  return true;
    if(vis[x][y])  return true;
    return false;
}

int bfs(node a, node b)
{
    clear_queue(q);
    ms(vis, 0);
    int cnt = 0, time1 = 0;//如果只有一个或者两个草 0就烧完了 参考样例3
    q.push(a);
    vis[a.x][a.y] = 1;
    
    if(a.x != b.x || a.y != b.y)//如果不是同一个点 将两个都入队
    {
        q.push(b);
        vis[b.x][b.y] = 1;
    }

    while(q.size())
    {
        node t = q.front();
        q.pop();
        cnt ++;//统计入队个数 用于判断 是否 遍历了所有的草

        int x = t.x, y = t.y;
        for(int i = 0; i < 4; i++)
        {
            int xx = x + d[i][0], yy = y + d[i][1];
            if(check(xx, yy))   continue;
            vis[xx][yy] = 1;
            q.push({xx, yy, t.step + 1});
            time1 = t.step + 1;//记录烧完所有草的时间
        }
    }
    
    if(cnt != len)//当没有遍历所有草的时候 直接返回-1
        return -1;
    return time1;
}

void solve()
{
    len = 0;
    ans = inf;
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i++) scanf("%s", g[i] + 1);

    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            if(g[i][j] == '#') 
                a[len].x = i, a[len].y = j, a[len].step = 0, len ++;//存储草的点 用于枚举
    
    for(int i = 0; i < len; i++)//遍历枚举两个点来开始烧(注意是可以取相同点
        for(int j = 0; j < len; j++)
        {
            int res = bfs(a[i], a[j]);
            if(res != -1)   ans = min(ans, res);//当成功遍历所有草 更新一次最小值
        }
    
    if(ans == inf)  ans = -1;//没有一次枚举能够遍历到所有的草
    printf("%d\n", ans);
}

int main()
{
    int _;
    scanf("%d", &_);
    for(int i = 1; i <= _; i++)
    {
        printf("Case %d: ", i);
        solve();
    }
    return 0;
}

10. UVA 11624 Fire!

首先 不止一个火 那就是多个火种同时扩散的BFS 这样其实很简单 刚开始的时候把全部火种入队就可以了 注意不能走重复的点(用 != inf)

然后对人BFS 只有当前步数小于火 人才能走出去(代替了人走到边界的dis 和 火的对比 减少了麻烦 而且人不能覆盖掉已经走过的路

第一次走到边界的时候 一定是最快的 直接返回步数即可

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>

using namespace std;
//多组输入的初始化 
//判断是否需要LL

#define endl '\n'
#define ms(x, y)    memset(x, y, sizeof x)
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
const int inf = 0x3f3f3f3f;
const LL INF = 0x3f3f3f3f3f3f3f3f;
int r, c, px, py;//row, col, personx, persony;
char g[1010][1010];
int per[1010][1010], fire[1010][1010];

int d[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

queue<PII> q;

void clear_queue(queue<PII>& q)//清空队列
{
    queue<PII> empty;
    swap(empty, q);
}

bool check(int x, int y)
{
    if(x < 1 || y < 1 || x > r || y > c)    return true;
    if(g[x][y] == '#')  return true;
    if(fire[x][y] != inf)   return true;
    return false;
}

void firebfs()
{
    while(q.size())
    {
        PII t = q.front();
        q.pop();
        
        int x = t.first, y = t.second;
        for(int i = 0; i < 4; i++)
        {
            int xx = x + d[i][0], yy = y + d[i][1];

            if(check(xx, yy)) continue;
            fire[xx][yy] = fire[x][y] + 1;
            q.push(make_pair(xx, yy));
        }
    }
}   

int run()
{
    clear_queue(q);
    q.push(make_pair(px, py));
    per[px][py] = 0;

    while(q.size())
    {
        PII t = q.front();
        q.pop();

        int x = t.first, y = t.second;
        if(x == 1 || x == r || y == 1 || y == c)   return per[x][y] + 1;//还差一步

        for(int i = 0; i < 4; i++)
        {
            int xx = x + d[i][0], yy = y + d[i][1];
            
            if(xx < 1 || yy < 1 || xx > r || yy > c)    continue;
            if(g[xx][yy] == '#')  continue;
            if(fire[xx][yy] <= per[x][y] + 1) continue;//比火走的慢
            if(per[xx][yy] <= per[x][y] + 1)    continue;//不能覆盖更快的点

            q.push(make_pair(xx, yy));
            per[xx][yy] = per[x][y] + 1;
        } 
    }
    return -1;//哼 想逃?
}
 
void solve()
{
    //init
    ms(per, 0x3f);
    ms(fire, 0x3f);

    scanf("%d%d", &r, &c);
    for(int i = 1; i <= r; i++) scanf("%s", g[i] + 1);

    for(int i = 1; i <= r; i++)
        for(int j = 1; j <= c; j++)
        {
            if(g[i][j] == 'J')  px = i, py = j;
            else if(g[i][j] == 'F')
            {
                fire[i][j] = 0;
                q.push(make_pair(i, j));//不止一个火种
            }
        }

    firebfs();//火BFS

    int res = run();//人BFS
    if(res == -1)   printf("IMPOSSIBLE\n");
    else printf("%d\n",res);

    clear_queue(q);
}

int main()
{
    int _;
    scanf("%d", &_);
    while(_--)
    {
        solve();
    }
    return 0;
}

11. POJ 3984 迷宫问题

http://poj.org/problem?id=3984

简单的BFS + 路径存储(递归)

使用前驱节点记录结构体点递归输出路径

int g[10][10];
bool st[10][10];
int d[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

struct node
{
    int x, y;
};
node pre[10][10];

bool check(int x, int y)
{
    if(x < 0 || y < 0 || x > 4 || y > 4 || st[x][y] == true || g[x][y] == 1 )    return true;
    return false;
}

void print(node a)
{
    if(a.x == 0 && a.y == 0)
    {
        printf("(%d, %d)\n", a.x, a.y);
        return;
    }

    else 
    {
        print(pre[a.x][a.y]);
        printf("(%d, %d)\n", a.x, a.y);
    }
}

void bfs()
{
    queue<node> q;
    q.push({0, 0});
    st[0][0] = 1;

    while(q.size())
    {
        node t = q.front();
        q.pop();

        if(t.x == 4 && t.y == 4)    return;

        for(int i = 0; i < 4; i++)
        {
            int xx = t.x + d[i][0], yy = t.y + d[i][1];

            if(check(xx, yy))   continue;
            st[xx][yy] = 1;
            q.push({xx, yy});
            pre[xx][yy] = t;//存储路径
        }
    }
}

int main()
{
    for(int i = 0; i < 5; i++)
        for(int j = 0; j < 5; j++)
            cin >> g[i][j];
    
    bfs();
    node a;
    a.x = 4, a.y = 4;
    print(a);
    
    return 0;
}

12. HDU 1241 Oil Deposits

深搜寻找连通块数量

注意找过的点要染色 来标志已经走过

int n, m, cnt;
char g[110][110];

int d[8][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {-1, -1}, {1, -1}, {-1, 1}};

bool check(int x, int y)
{
    if(x < 1 || y < 1 || x > n || y > m)    return true;
    if(g[x][y] == '*')  return true;
    return false;
}

void dfs(int x, int y)
{
    g[x][y] = '*';
    
    for(int i = 0; i < 8; i++)
    {
        int xx = x + d[i][0], yy = y + d[i][1];
        if(check(xx, yy))   continue;
        dfs(xx, yy);
    }
}

int main()
{
    while(scanf("%d%d", &n, &m))
    {
        cnt = 0;
        if(n == 0 && m == 0)    break;
        
        for(int i = 1; i <= n; i++)     scanf("%s", g[i] + 1);
        
        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
                if(g[i][j] == '@')
                {
                    cnt ++;
                    dfs(i, j);
                }
                
        printf("%d\n",cnt);
    }
    return 0;    
}

13. HDU 1495 非常可乐

是第7题的翻新版 题意更晦涩一些

提炼一下就是 使大瓶子和可乐瓶中的水相等且小瓶子中没有水

这题的操作只有互相倒水 (装满或者倒完

为了方便枚举 选择了用数组存储三个杯子 这样我们就可以用循环来枚举操作了

也可以手写 1,2 1,3 2,1 2,3 3,1 3,2 六次操作

int w[3];//容积
bool st[110][110][110];

struct node 
{
    int v[3];//因为用数组存储便于枚举
    int step;
};


void bfs()
{
    ms(st, false);

    queue<node> q;
    q.push({w[0], 0, 0, 0});
    st[w[0]][0][0] = true;

    while(q.size())
    {
        node t = q.front();
        q.pop();

        if(t.v[0] == t.v[2] && t.v[1] == 0)
        {
            cout << t.step << endl;
            return;
        }

        for(int i = 0; i < 3; i++)
            for(int j = 0; j < 3; j++)
            {
                if(i != j)
                {
                    node temp = t;
                    int minn = min(temp.v[i], w[j] - temp.v[j]);//从i倒水到j
                    temp.v[i] -= minn, temp.v[j] += minn;

                    if(!st[temp.v[0]][temp.v[1]][temp.v[2]])
                    {
                        temp.step ++;
                        q.push(temp);
                        st[temp.v[0]][temp.v[1]][temp.v[2]] = true;
                    }
                }
            }
    }
    cout << "NO" << endl;
}

int main()
{
    while(cin >> w[0] >> w[1] >> w[2])
    {
        if(w[0] == 0 && w[1] == 0 && w[2] == 0)  break;

        if(w[1] > w[2]) swap(w[1], w[2]);

        if(w[0] % 2 == 1)//可乐是奇数    
            cout << "NO" << endl;
        else bfs();
    }
    return 0;
}

14.HDU 2612 Find a way

很简单的两次BFS问题

注意的是步数的叠加计算和一些细节

细节1 M可以经过Y Y可以经过M

细节2 kfc 步数为0时虽然是距离最小 但是根本无法到达 所以不能算在内

int n, m, k;
char g[210][210];
int kfc[40010];//存kfc的步数
bool st[210][210];
int dis[210][210];

int d[4][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}};

bool check(int x, int y)
{
    if( x < 1 || y < 1 || x > n || y > m )  return true;
    if(g[x][y] == '#')  return true;
    if(st[x][y] == 1)   return true;//这里要注意 M可以经过Y  Y可以经过M
    return false;
}

void bfs(int x, int y)
{
    ms(st, false);
    ms(dis, 0);
    k = 0;
    queue<PII> q;
    q.push({x, y});
    st[x][y] = 1;
    dis[x][y] = 0;

    while(q.size())
    {
        PII t = q.front();
        q.pop();

        for(int i = 0; i < 4; i++)
        {
            int xx = t.first + d[i][0], yy = t.second + d[i][1];
            if(check(xx, yy))   continue;

            st[xx][yy] = 1;
            dis[xx][yy] = dis[t.first][t.second] + 1;
            q.push({xx, yy});
        }
    }

    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++)
            if(g[i][j] == '@' && dis[i][j])//dis不能为0 因为走不到不能算
                kfc[k++] += dis[i][j];// 注意+=
}

int main()
{
    while(scanf("%d%d", &n, &m) == 2)
    {
        int minn = inf;
        ms(kfc, 0);
        for(int i = 1; i <= n; i++) scanf("%s", g[i] + 1);

        for(int i = 1; i <= n; i++)
            for(int j = 1; j <= m; j++)
            {
                if(g[i][j] == 'Y')  bfs(i, j);
                else if(g[i][j] == 'M') bfs(i ,j);
            }

        for(int i = 0; ; i++)
        {
            if(kfc[i] == 0) break;
            minn = min(minn, kfc[i]);
        }

        printf("%d\n", minn * 11);
    }
    return 0;
}

完结撒花 完结日期 2022/2/11

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值