【codevs 1004】四子连棋

12 篇文章 1 订阅
2 篇文章 0 订阅

题目描述 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

需要用到哈希判重。将局面看作16位的三进制数,转化为十进制后取模作为key,已经达到过的局面不再重复入队。
队列中存的是整个状态,包括两个空格的坐标,达到当前局面需要的步数,上一步走的是黑棋还是白棋。
黑白双方交替走棋
最终局面的判断要考虑全面,包括行、列、两种斜线。
新状态入队时,要更新与局面有关的所有元素。
由于一开始不能明确先走黑棋还是先走白棋,所以两种走法取最小值。

看到洛谷的讨论区里说,对于洛谷的数据,如果初始状态就是目标状态,则答案为1.
觉得毫无道理。
codevs加特判才能A,,,,
这里写图片描述

这个数据在本地=3,交上就是这样。。。
一定有什么奇奇怪怪的原因¥%…………&%……#¥……#……%¥&#%&¥¥……#%@!##@@¥!@%¥#……%¥#……¥%&*()()&……&¥%&¥%……#%#%@¥#%#¥……%&¥#%……#¥%……%¥&%……&%……&……*

代码先贴着

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
using namespace std;
const int mod=1000000+9999;
int dx[]={0,0,1,0,-1},dy[]={0,1,0,-1,0};
char s[5][5];
bool vis[1956781];
struct node
{
    int x1,y1,x2,y2,a[5][5],step,up;
}re;
bool can(int x,int y)//是否可行 
{
    if(x<1||y<1||x>4||y>4) return 0;
    return 1;
}
bool equal(int a,int b,int c,int d)//判断四个数是否相等
{
    if(a!=b||b!=c||c!=d) return 0;
    return 1; 
}
bool pd(node now)//判断是否为最终局面 
{
    if(equal(now.a[1][1],now.a[2][2],now.a[3][3],now.a[4][4])) return 1;
    if(equal(now.a[1][4],now.a[2][3],now.a[3][2],now.a[4][1])) return 1;
    for(int i=1;i<=4;i++)
    {
        if(equal(now.a[i][1],now.a[i][2],now.a[i][3],now.a[i][4])) return 1;
        if(equal(now.a[1][i],now.a[2][i],now.a[3][i],now.a[4][i])) return 1;
    }
    return 0;
}
int hash(node now)
{
    int key=0;
    for(int i=1;i<=4;i++)
    for(int j=1;j<=4;j++)
    key=(key*3+now.a[i][j])%mod;
    return key;
}
int bfs()
{
    memset(vis,0,sizeof(vis));
    queue<node>q;
    while(!q.empty()) q.pop();
    re.step=0;
    q.push(re);
    vis[hash(re)]=1;
    while(!q.empty())
    {
        node now=q.front();
        q.pop();
        if(pd(now)) return now.step;
        else
        for(int i=1;i<=8;i++)
        {
            int fx,fy;
            if(i<=4) fx=now.x1+dx[i],fy=now.y1+dy[i];
            else fx=now.x2+dx[i-4],fy=now.y2+dy[i-4];
            node nxt=now;
            if(can(fx,fy)&&now.a[fx][fy]==now.up)
            {
                if(i<=4) swap(nxt.a[fx][fy],nxt.a[now.x1][now.y1]);//保证两个空格都被搜到 
                else swap(nxt.a[fx][fy],nxt.a[now.x2][now.y2]);
                if(!vis[hash(nxt)])//判重 
                {
                    if(i<=4) nxt.x1=fx,nxt.y1=fy;//更新下一个状态的每一个元素 
                    else nxt.x2=fx,nxt.y2=fy;
                    nxt.step=now.step+1;
                    vis[hash(nxt)]=1;
                    nxt.up=3-now.up;
                    q.push(nxt);
                }
            }
        }
    }
}
int main()
{
    for(int i=1;i<=4;i++)
    for(int j=1;j<=4;j++)
    {
        cin>>s[i][j];
        if(s[i][j]=='O')
        {
            if(!re.x1) re.x1=i,re.y1=j;
            else re.x2=i,re.y2=j;
            re.a[i][j]=0;
        }
        else if(s[i][j]=='B') re.a[i][j]=1;
        else if(s[i][j]=='W') re.a[i][j]=2;
    }
    int ans;
    re.up=1;
    ans=bfs();
    re.up=2;
    printf("%d\n",min(ans,bfs()));
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值