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;
    }
  • 相关阅读:
    C语言-if语句
    C语言-表达式
    C语言-基础
    Java for LeetCode 187 Repeated DNA Sequences
    Java for LeetCode 179 Largest Number
    Java for LeetCode 174 Dungeon Game
    Java for LeetCode 173 Binary Search Tree Iterator
    Java for LeetCode 172 Factorial Trailing Zeroes
    Java for LeetCode 171 Excel Sheet Column Number
    Java for LeetCode 169 Majority Element
  • 原文地址:https://www.cnblogs.com/LLTYYC/p/9625819.html
Copyright © 2011-2022 走看看