zoukankan      html  css  js  c++  java
  • P4289 [HAOI2008]移动玩具

    传送门

    广搜

    4*4 的方阵只有 0 和 1

    显然可以状态压缩

    (如样例的开始状态压缩后就是1111000011100010)

    为了加快速度用了双向广搜(顺便学了一下双向广搜)

    双向广搜顾名思义

    就是从起点和终点两个方向广搜

    每次选择扩展步数少的扩展一层

    然后一旦一个状态被两边都找到了

    那就把两边的步数加一下,就是答案了

    然后要注意位运算的细节

    具体实现看代码吧

    #include<iostream>
    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<cmath>
    #include<queue>
    using namespace std;
    struct node
    {
        int p,stp;
    };//广搜的队列,p表示状态,stp表示当前走了几步
    queue <node> q[2];
    bool pd[100007][2];//记忆化,pd[i][]为到i状态需要的最少步数
    int ans,st,lst;
    inline void add(int k,int p,int stp)
    //尝试把状态p加入队列
    {
        if(pd[p][k^1]||(p==st&&k)||(p==lst&&k==0))//如果另一边已经找过了或者到了另一边的起点
        //因为在起点和终点的pd为0 所以要特判一下
        {
            ans=stp+pd[p][k^1];//更新ans
            return;//直接返回
        }
        if(pd[p][k]||p==st||p==lst) return;//如果找过了或者回到开始点,直接返回
    
        //否则
        pd[p][k]=stp;//更新pd
        node t; t.p=p; t.stp=stp;
        q[k].push(t);//加入队列
    }
    inline void bfs()
    {
        int k= q[1].size()<q[0].size();//确定要从哪一边扩展
        int now=q[k].front().stp;
        while(!q[k].empty())
        {
            node t=q[k].front();
            if(t.stp>now||ans) break;//保证一次只扩展一层
            q[k].pop();//(细节)要先判断再弹出
            for(int i=15;i>=0;i--)//向左移动
            {
                if(i%4==3) continue;//注意如果在边界就不能动
                if( !(t.p& (1<<i) ) || t.p& ( 1<<(i+1) ) ) continue;//判断
                add(k, t.p^ (1<<i) ^ ( 1<<(i+1) ), t.stp+1);//直接异或一波得到下一步的状态
            }
            for(int i=15;i>=0;i--)//向右
            {
                if(i%4==0) continue;//同样判断
                if( !(t.p& (1<<i) ) || t.p& (1<<(i-1) ) ) continue;
                add(k, t.p^ (1<<i) ^ ( 1<<(i-1) ), t.stp+1);
            }//同上
            for(int i=11;i>=0;i--)//向上,注意i的范围
            {
                if( !(t.p& (1<<i) ) || t.p& (1<<(i+4) ) ) continue;
                add(k, t.p^ (1<<i) ^ ( 1<<(i+4) ), t.stp+1);
            }//同上
            for(int i=15;i>=4;i--)//向上,同样注意i
            {
                if( !(t.p& (1<<i) ) || t.p& (1<<(i-4) ) ) continue;
                add(k, t.p^ (1<<i) ^ ( 1<<(i-4) ), t.stp+1);
            }//同上
        }
    }
    int main()
    {
        char ss[7]; memset(ss,0,sizeof(ss));
        for(int i=0;i<4;i++)
        {
            cin>>ss;
            for(int j=0;j<4;j++)
                st+=( (ss[j]-'0')<<(15-i*4-j) );
        }//读入状态
        node t; t.p=st; t.stp=0;
        q[0].push(t);//开始状态压入队列 
    
        for(int i=0;i<4;i++)
        {
            cin>>ss;
            for(int j=0;j<4;j++)
                lst+=( (ss[j]-'0')<<(15-i*4-j) );
        }
        t.p=lst; q[1].push(t);//同上
    
        if(st==lst)//特判一波起点和终点状态相同的情况
        {
            cout<<0;
            return 0;
        }
    
        while(!ans)
            bfs();//广搜找ans
        cout<<ans;
    }
  • 相关阅读:
    从零开始学VUE之组件化开发(组件数据的存放)
    从零开始学VUE之组件化开发(组件分离写法)
    从零开始学VUE之组件化开发(语法糖优化组件注册)
    进程与线程的一个简单解释
    Crontab爬虫定时执行
    接口分类复习
    最长公共子串
    最长公共子序列(力扣第1143题)
    Reduce端分组排序源码分析
    Job提交流程源码和切片源码详解
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9625819.html
Copyright © 2011-2022 走看看