zoukankan      html  css  js  c++  java
  • BZOJ 1054题解 BFS暴力求解

    BZOJ 1054题解 BFS暴力求解

    1054: [HAOI2008]移动玩具

    Time Limit: 10 Sec  Memory Limit: 162 MB
    Submit: 1884  Solved: 1033
    [Submit][Status][Discuss]

    Description

      在一个4*4的方框内摆放了若干个相同的玩具,某人想将这些玩具重新摆放成为他心中理想的状态,规定移动
    时只能将玩具向上下左右四个方向移动,并且移动的位置不能有玩具,请你用最少的移动次数将初始的玩具状态移
    动到某人心中的目标状态。

    Input

      前4行表示玩具的初始状态,每行4个数字1或0,1表示方格中放置了玩具,0表示没有放置玩具。接着是一个空
    行。接下来4行表示玩具的目标状态,每行4个数字1或0,意义同上。

    Output

      一个整数,所需要的最少移动次数。

    Sample Input

    1111
    0000
    1110
    0010

    1010
    0101
    1010
    0101

    Sample Output

    4

      这道题第一眼看过去就跟codevs的四子连棋很像,只不过这道题的空白点数量不确定。对于求最短步数的搜索我们可以迭代加深搜索或者bfs,但针对这道题迭代的话过于麻烦,那么我们就直暴力bfs+判重。对于判重,我们可以分析下棋盘,棋盘只有4x4十六个点,每个点只有0,1两种状态,我们可以将每种棋盘的状况用一个数字存下来,这个数字不会超过 216   (大概六万多一点),然后用一个status数组判断这种情况是否访问过。

    #include<cstdio>
    #include<deque>
    using namespace std;
    int status[70000];              //用来判重
    int nums,nume;
    struct STATUS{
        int t[5][5];
        int step;
    }sts[2];                        //用结构体存棋盘和步数
    int dx[]={ 1, 0,-1, 0};
    int dy[]={ 0, 1, 0,-1};
    deque<STATUS>q;                 //队列queue的内部其实是deque...直接用deque可能快上那么一丢丢
    bool check(STATUS temp){            //判重函数
        int num=0;
        for(int i=1;i<=4;i++)
            for(int j=1;j<=4;j++)
                num=(num<<1)+temp.t[i][j];  //转十进制
        if(status[num]==1)return false;
        else return status[num]=true;
    }
    bool cmp(STATUS a,STATUS b){   //比较两个棋盘是否相等
        for(int i=1;i<=4;i++)
            for(int j=1;j<=4;j++){
                if(a.t[i][j]!=b.t[i][j])return false;
            }
        return true;
    }
    void search(){
        sts[0].step=0;
        q.push_back(sts[0]);   
        while(!q.empty()){
            STATUS curr=q.front();q.pop_front();
            for(int i=1;i<=4;i++)
                for(int j=1;j<=4;j++){
                    if(curr.t[i][j]==1){
                        for(int k=0;k<4;k++){
                            int kx=dx[k]+i;
                            int ky=dy[k]+j;
                            if(kx>0&&ky>0&&ky<5&&kx<5&&curr.t[kx][ky]==0){
                                STATUS copy=curr;
                                swap(copy.t[i][j],copy.t[kx][ky]);
                                ++copy.step;
                                if(check(copy)){
                                    q.push_back(copy);
                                    if(cmp(copy,sts[1]))return ;//如果达到目标棋盘则直接return
    } } } } } } }
    void input(){ char c[6]; nums=0;nume=0; for(int i=1;i<=4;i++){ scanf("%s",c); scanf(" "); for(int j=0;j<4;j++){ if(c[j]=='1')sts[0].t[i][j+1]=1; nums=(nums<<1)+c[j]-'0'; } } status[nums]=1; for(int i=1;i<=4;i++){ scanf("%s",c); if(i!=4)scanf(" "); for(int j=0;j<4;j++){ if(c[j]=='1')sts[1].t[i][j+1]=1; nume=(nume<<1)+c[j]-'0'; } } status[nume]=0; } int main(){ input(); if(cmp(sts[1],sts[0])){ //特判一下起始棋盘和目标棋盘是否是一样的,不然会迷之WA printf("0"); return 0; } search(); printf("%d",q.back().step); }

      其实这道题还有别的思路,我们可以将每种情况枚举出来,然后建图。 读入时,用cnt记录棋盘中1的数目,再枚举一下所有有cnt个1的棋盘,每个棋盘用它的二进制数字作为序号来建点。如果两个棋盘只有1步之差,那么我们给他们连上一个权值为1的边。连边完成后,我们就可以用spfa或dijkstra来求起始棋盘到目标棋盘的最短路。这个最短路就是最短步数。

      伪代码如下

    那个什么,建图连边太麻烦,我懒得写了。。。这道题明显就很水,随便bfs一下就行。。。实在想建图的慢慢建吧哈哈哈哈哈哈哈
    View Code

  • 相关阅读:
    HTML技巧: 语义化你的代码
    css sprite
    Redis主从复制原理
    idea修改快捷键
    Ubuntu14.20 安装docker,创建centos6.7容器,并访问centos容器
    [转]SQL 中 with as 的用法
    ftp与sftp及sftp和scp的区别
    Linux top 命令
    Linux free 命令
    ubuntu 源方式 安装jdk
  • 原文地址:https://www.cnblogs.com/alpharplediz/p/5960744.html
Copyright © 2011-2022 走看看