poj1753

      从今天开始,真的要一步一脚印,从最简单的做起,争取最短时间进步!!!

     题意大概是给你一个4×4棋盘,棋盘上不是‘b'就是'w',要你用最少的翻转步数是它们全部变成'w'或全部变成'b';每次翻转时,该位置的上,下左,右也会随着翻转。

   看了别人家的题解才想出来的。。。

   大概思路是位运算加bfs;首先4×4不大,所以考虑把棋盘上的字母转换成一个二进制数字,每一个数字对应着一个棋盘的状态,如果全部是1,即全部是’b'此时的数字是65535,反过来全部是‘w',数字是0;所以在输入棋盘时,首先把棋盘的状态转换成一个数字表示,其实就是求这个数字经过最少的变化次数到达65535或0;

   对于棋盘每一个状态,都去枚举它可能翻转的棋子,由于对于每一颗棋子,只有翻与不翻之分,偶数次其实就是不翻,为了达到最少的步数就必须避免重复前面的状态,这里还用了一个数组进行标记。对于从第一颗到第16颗棋子,翻的时候对周围都会造成影响,先假设前一个状态为: wwww  wwww  wwww  wwww 即二进制表示为0000 0000 0000 0000,十进制对应为0。若此时选定左上角第一个棋子进行操作,根据规则,它右边和下边的也要同时进行变换(因为其左边和上边为空,不做考虑),之后,相应的状态用二进制表示,应变为:1100 1000 0000 0000,十进制值为51200。这个过程相当于对十进制数51200进行对十进制数0的异或操作,即next=0^(51200),而51200这个数则可以根据对十进制数1进行相应的左移操作得到。同时,我们知道,棋牌总共有16个位置,也就是说相应的不同的操作也有16种,即有16个不同的数经过异或操作用来改变前一个状态的值。那么,就先将这16个数枚出来吧,代码如下:

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

void init()
{
    memset(change,0,sizeof(change));
    for(int i=0;i<4;i++)
        for(int j=0;j<4;j++)
        {
            change[4*i+j]^=(1<<((3-i)*4+3-j));
            for(int k=0;k<4;k++)
            {
                int a=i+dir[k][0];
                int b=j+dir[k][1];
                if(a<0||a>3||b<0||b>3)continue;
                change[4*i+j]^=(1<<((3-a)*4+3-b));
            }
        }
        for(int i=0;i<16;i++)
            {
            cout<<change[i]<<' ';
        cout<<endl;
            }
}

主代码:

#include <iostream>
#include <cstring>
#include <string>
#include <cmath>
#include <algorithm>
#include <queue>
#include <map>
#include <vector>
#include <cstdio>
#include <cstdlib>
using namespace std;
char a[4][4];
int change[16]={51200,58368,29184,12544,
35968,20032,10016,4880,
2248,1252,626,305,
140,78,39,19
};
bool visit[65537];
//位运算加bfs
struct node
{
    int state;
    int step;
};
int solve(int s)
{
    memset(visit,false,sizeof(visit));
    queue<node>q;
    node cur,tmp;
    cur.state=s;
    cur.step=0;
    q.push(cur);
    visit[s]=true;
    //if(cur.state==0||cur.state==65535)return cur.step;
    while(!q.empty())
    {
        cur=q.front();
        q.pop();
        if(cur.state==0||cur.state==0xffff)return cur.step;
        for(int i=0;i<16;i++)
        {
            tmp.state=cur.state^change[i];
            tmp.step=cur.step+1;
            if(visit[tmp.state])continue;
            if(tmp.state==0||tmp.state==0xffff)return tmp.step;
                q.push(tmp);
                visit[tmp.state]=true;
        }
    }
    return -1;
}
int main()
{
    while(scanf("%s",a[0])!=EOF)
    {
        for(int i=1;i<4;i++)scanf("%s",a[i]);
        int st=0;
        for(int i=0;i<4;i++)
            for(int j=0;j<4;j++)
            {
               if(a[i][j]=='b')
                st^=(1<<((3-i)*4+3-j));
            }
            //cout<<state<<endl;


            int sum=solve(st);
            if(-1==sum)cout<<"Impossible"<<endl;
            else  cout<<sum<<endl;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值