codevs 1004 四子连棋(极难)

时间限制: 1 s
空间限制: 128000 KB
题目等级 : 黄金 Gold
题解
题目描述 Description
在一个4*4的棋盘上摆放了14颗棋子,其中有7颗白色棋子,7颗黑色棋子,有两个空白地带,任何一颗黑白棋子都可以向上下左右四个方向移动到相邻的空格,这叫行棋一步,黑白双方交替走棋,任意一方可以先走,如果某个时刻使得任意一种颜色的棋子形成四个一线(包括斜线),这样的状态为目标棋局。

● ○ ●
○ ● ○ ●
● ○ ● ○
○ ● ○

输入描述 Input Description
从文件中读入一个4*4的初始棋局,黑棋子用B表示,白棋子用W表示,空格地带用O表示。
输出描述 Output Description
用最少的步数移动到目标棋局的步数。

样例输入 Sample Input
BWBO
WBWB
BWBW
WBWO

样例输出 Sample Output
5

数据范围及提示 Data Size & Hint
hi

思路:由于棋盘只有4*4,所以我们可以用超级暴力的BFS来解决(甚至连剪枝都不用)。
首先我们观察棋盘,只有当格子为空时才能移动棋子,所以我们从空白格子开始,向四个方向枚举移动棋子,注意,由于题目中说黑白双方交替走棋,所以我们要在当前状态中记录变量next,意为可移动棋子的种类(就是当前回合可以移动哪种颜色的棋子),并交替更换next。然后就是判重,把棋盘转化成三进制数,用哈希记录状态并判断是否重复,每到一个状态就判断是否为目标棋局,如果是的话,直接退出并输出答案,因为根据BFS的特性,当前如果满足条件,那么一定是最优的答案。
还需要注意一点,由于题目中说一开始任意一方可以先走,所以最初要把两个状态放入队列中。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int maxn=5000+5;
const int mod=3e6+7;
struct zt{
    int ma[5][5];
    int step,next;
}s,f;
int ans;
const int dx[]={0,1,0,-1};
const int dy[]={1,0,-1,0};
queue<zt> q;
bool ha[mod+10];
bool equ(int x1,int x2,int x3,int x4)
{
    if(x1==x2&&x2==x3&&x3==x4) return 1;
    return 0;
}
bool finish()
{
    for(int i=1;i<=4;i++)
    {
        if(equ(f.ma[i][1],f.ma[i][2],f.ma[i][3],f.ma[i][4])) return 1;
        if(equ(f.ma[1][i],f.ma[2][i],f.ma[3][i],f.ma[4][i])) return 1;
    }
    if(equ(f.ma[1][1],f.ma[2][2],f.ma[3][3],f.ma[4][4])) return 1;
    if(equ(f.ma[1][4],f.ma[2][3],f.ma[3][2],f.ma[4][1])) return 1;
    return 0;
}
int gethash(zt z)
{
    int ret=0,key=1;
    for(int i=1;i<=4;i++)
        for(int j=1;j<=4;j++)
        {
            ret+=z.ma[i][j]*key;
            key*=3;
        }
    ret%=mod;
    return ret;
}
bool can(int x,int y)
{
    int nxt=f.next;
    if(x<=0||x>4||y<=0||y>4) return 0;
    if(f.ma[x][y]==nxt) return 1;
    return 0;
}
void move(int x,int y)
{
    for(int i=0;i<4;i++)
    {
        int zx=x+dx[i],zy=y+dy[i];
        if(can(zx,zy))
        {
            zt t=f;
            swap(t.ma[x][y],t.ma[zx][zy]);
            if(!ha[gethash(t)])
            {
                t.step=f.step+1;
                if(f.next==1) t.next=2;
                if(f.next==2) t.next=1;
                q.push(t);
            }
        }
    }
}
void bfs()
{
    q.push(s);
    s.next=2;
    q.push(s);
    while(!q.empty())
    {
        f=q.front();
        if(finish())
        {
            ans=f.step;
            return;
        }
        ha[gethash(f)]=1;
        q.pop();
        for(int i=1;i<=4;i++)
            for(int j=1;j<=4;j++)
            {
                if(!f.ma[i][j]) move(i,j);
            }
    }
}
int main()
{
    memset(ha,0,sizeof(ha));
    char ch;
    for(int i=1;i<=4;i++)
        for(int j=1;j<=4;j++)
        {
            cin>>ch;
            if(ch=='B') s.ma[i][j]=1;
            else if(ch=='W') s.ma[i][j]=2;
            else if(ch=='O') s.ma[i][j]=0;
        }
    s.step=0;
    s.next=1;
    bfs();
    if(!ans) ans=1;
    printf("%d",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值