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 % 
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值