SGU 536 Berland Chess 状态压缩 + BFS

题目大意:

就是现在有一个n*m的棋盘,1 <= n , m <= 15, 棋盘上你有一个棋子就是国王,用 * 表示, . 表示空的格子, 然后B代表敌方的象,K代表敌方的马,R代表敌方的车

现在走的规则是国王每次可以走到自己周围的8个格子(水平,竖直或者对角线的方向), 象可以向对角线方向走任意格,车可以竖直或者水平方向走任意格,马可以从(x, y)位置走到(x + 1, y + 2), (x - 1, y + 2), (x - 1, y - 2), (x + 1, y - 2), (x +2, y - 1), (x + 2, y + 1), (x - 2, y + 1), (x - 2, y - 1)这8个位置

现在要求你操作国王,在不走到任何能被当前的敌方棋子吃掉的格子的情况下,逐个吃掉敌方的棋子至少需要走的步数,这里由于程序bug所以在你操作国王时,你走一步后敌方棋子都不会移动


大致思路:

首先这道题很明显可以考虑用到BFS, 这样就需要考虑每个格子能否走到下一个格子

由于本题中整个棋盘上的棋子数量不会超过15,可以用状态压缩的思想来判断当前这个格子能否走

用yes[ x ][ y ][ p ]表示对于坐标为 x, y 的点,走上去需要满足状态 p, p的二进制的每一位代表是否需要吃掉对应编号的棋子(比如 二进制数 101代表需要吃点棋子1和3才能走上位置(x, y)

那么在BFS的时候,判断周围的位置的yes数组值来决定能否进入

对于搜索的时候用vis[ x ][ y ][ p ] 记录进入位置 (x, y)且状态为 p 的情况已经访问过,不用继续加入队列访问


注意处理时出现的棋子相互阻挡前进的情况


Result  :  Accepted     Memory  :  85118 KB     Time  :  280 ms

/*
 * Author: Gatevin
 * Created Time:  2014/9/2 14:38:56
 * File Name: I.cpp
 */
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
using namespace std;
const double eps(1e-8);
typedef long long lint;

#define x first
#define y second
#define mp(a, b) make_pair(a, b)
#define clr(x) memset(x, 0, sizeof(x));

queue <pair<pair<int, int>, int> > q;
int step[16][16][1 << 16];//走到(x, y)且状态为p的步数
bool vis[16][16][1 << 16];//记忆化
int yes[16][16];//进入位置(x, y)需要满足的状态
char maz[16][16];
int cnt;//敌方棋子数量
map <pair<int, int>, int> M;//敌方位于位置(x, y)的棋子的编号
int n,m;
int sx, sy;//起点坐标

int dx[] = {1, 1, 1, 0, 0, -1, -1, -1};//国王走8个方向
int dy[] = {1, 0, -1, 1, -1, 1, 0, -1};

void bfs()
{
    q.push(mp(mp(sx, sy), 0));
    vis[sx][sy][0] = 1;
    while(!q.empty())
    {
        for(int i = 0; i < 8; i++)
        {
            int tx = q.front().x.x + dx[i];
            int ty = q.front().x.y + dy[i];
            int tp = q.front().y;
            if(tx >= 1 && tx <= n && ty >= 1 && ty <= m && ((tp & yes[tx][ty]) == yes[tx][ty]) && !vis[tx][ty][tp])
            {
                vis[tx][ty][tp] = 1;//上一状态的终止
                if(maz[tx][ty] == 'K' || maz[tx][ty] == 'B' || maz[tx][ty] == 'R')
                {
                    tp = tp | (1 << M[mp(tx, ty)]);//杀死了敌方棋子,状态更新
                }
                vis[tx][ty][tp] = 1;//下一状态的开始
                step[tx][ty][tp] = step[q.front().x.x][q.front().x.y][q.front().y] + 1;
                q.push(mp(mp(tx, ty), tp));
            }
        }
        q.pop();
    }
    int answer = 0x7fffffff;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            if(maz[i][j] == 'R' || maz[i][j] == 'B' || maz[i][j] == 'K')
            {
                if(vis[i][j][(1 << cnt) - 1])//杀死敌方所有棋子的状态
                {
                    answer = min(answer, step[i][j][(1 << cnt) - 1]);
                }
            }
        }
    }
    if(answer == 0x7fffffff)
    {
        printf("-1\n");
    }
    else
    {
        printf("%d\n", answer);
    }
    return;
}

void flagR(int nx, int ny)//车
{
    int i = nx - 1;
    while(i >= 1)
    {
        if(maz[i][ny] != '.')
        {
            yes[i][ny] += (1 << M[mp(nx, ny)]);
            break;
        }
        else
        {
            yes[i][ny] += (1 << M[mp(nx, ny)]);
            i--;
        }
    }
    i = nx + 1;
    while(i <= n)
    {
        if(maz[i][ny] != '.')
        {
            yes[i][ny] += (1 << M[mp(nx, ny)]);
            break;
        }
        else
        {
            yes[i][ny] += (1 << M[mp(nx, ny)]);
            i++;
        }
    }
    int j = ny - 1;
    while(j >= 1)
    {
        if(maz[nx][j] != '.')
        {
            yes[nx][j] += (1 << M[mp(nx, ny)]);
            break;
        }
        else
        {
            yes[nx][j] += (1 << M[mp(nx, ny)]);
            j--;
        }
    }
    j = ny + 1;
    while(j <= m)
    {
        if(maz[nx][j] != '.')
        {
            yes[nx][j] += (1 << M[mp(nx, ny)]);
            break;
        }
        else
        {
            yes[nx][j] += (1 << M[mp(nx, ny)]);
            j++;
        }
    }
    return;
}

void flagB(int nx, int ny)
{
    int d1[] = {1, 1, -1, -1};//象走四个方向
    int d2[] = {1, -1, 1, -1};
    for(int i = 0; i < 4; i++)
    {
        int tx = d1[i] + nx;
        int ty = d2[i] + ny;
        while(tx >= 1 && tx <= n && ty >= 1 && ty <= m)
        {
            if(maz[tx][ty] != '.')
            {
                yes[tx][ty] += (1 << M[mp(nx, ny)]);
                break;
            }
            else
            {
                yes[tx][ty] += (1 << M[mp(nx, ny)]);
                tx += d1[i];
                ty += d2[i];
            }
        }
    }
    return;
}

void flagK(int nx, int ny)//马
{
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            if((abs(i - nx) == 1 && abs(j - ny) == 2) || (abs(i - nx) == 2 && abs(j - ny) == 1))
            {
                yes[i][j] += (1 << M[mp(nx, ny)]);
            }
        }
    }
    return;
}

int main()
{
    clr(yes);
    clr(vis);
    clr(step);
    cnt = 0;
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; i++)
    {
        getchar();
        for(int j = 1; j <= m; j++)
        {
            scanf("%c", &maz[i][j]);
            if(maz[i][j] == '*')
            {
                sx = i; sy = j;
            }
            if(maz[i][j] == 'R' || maz[i][j] == 'K' || maz[i][j] == 'B') M[mp(i, j)] = cnt++;
        }
    }
    if(cnt == 0)//无敌方棋子
    {
        printf("0\n");
        return 0;
    }
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
             switch(maz[i][j])//处理各个格子进入需要的状态
             {
                 case 'R' : flagR(i, j); break;
                 case 'B' : flagB(i, j); break;
                 case 'K' : flagK(i, j); break;
             }
        }
    }
    bfs();
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值