1 条题解

  • 0
    @ 2025-8-24 21:23:39

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar 坏耶
    **

    搬运于2025-08-24 21:23:38,当前版本为作者最后更新于2020-01-08 22:43:40,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    萌新日常打卡

    思路很简单,就模拟

    题目要你干什么那就干什么(废话)
    大部分的题解已经讲了怎么模拟了
    不过我还是我还是讲一遍吧
    • 模拟思路
    • 枚举所有可能的移动,用搜索的方法即可,建议使用dfs
    • 重要的部分在时间优化上(虽然我不知道不优化能不能过)

    关于剪枝
    首先搜索移动的时候,并不用全部往下搜,
    如果左边有块,不向左移动(这个各位大佬都讲了)
    因为左边的块会向右移动且字典序更小
    我想说的是,另一个剪枝!
    现在如果我说不可以剪交换相同各位可以理解吧
    但是交换相同颜色其实是可以优化的
    可以发现无论交换哪个地方的相同颜色结果相同
    所以只要保留字典序最小的交换相同颜色即可
    就是每层dfs搜到第一次相同的就允许,再搜到就剪掉
    这样既可以优化又不会被故意浪费步数的数据卡

    上代码

    #include<bits/stdc++.h>
    using namespace std;
    int read()//快速读入
    {
        int sum=0,flag=1;
        char ch=getchar();
        while(ch>'9'||ch<'0'){if(ch=='-')ch=getchar();ch=getchar();}
        while(ch<='9'&&ch>='0'){sum=sum*10+ch-'0';ch=getchar();}
        return sum*flag;
    }
    struct node{int x,y,z;}c[7];//储存答案
    int  n,a[7][9],    b[7][7][9];
    //   读入数据   存放dfs前的备份数据
    queue<node>q;
    //消除块的队列(也可以用bool数组,但我觉得这样消除时快将近一半的时间)
    void fz(int k)//从a备份内容到b
    {
        for(int i=1;i<=5;i++)
        for(int j=1;j<=7;j++)
        b[k][i][j]=a[i][j];
    }
    void zf(int k)//从b还原内容到a
    {
        for(int i=1;i<=5;i++)
        for(int j=1;j<=7;j++)
        a[i][j]=b[k][i][j];
    }
    bool jc()//检测是不是没有块了
    {
        for(int i=1;i<=5;i++)//只要检测最下排即可
        if(a[i][1])return 0;
        return 1;
    }
    bool xc()//消除判定
    {
        for(int i=1;i<=5;i++)for(int j=1;j<=7;j++)
        {//判定可消除,一定不要直接赋值0
            if(a[i][j]&&a[i-1][j]==a[i][j]&&a[i+1][j]==a[i][j])q.push({i,j,0});
            if(a[i][j]&&a[i][j-1]==a[i][j]&&a[i][j+1]==a[i][j])q.push({i,j,1});
        }
        if(q.empty())return 0;//没有动
        while(!q.empty())//处理删除的块
        {
            node k=q.front();q.pop();
            if(k.z)a[k.x][k.y]=a[k.x][k.y-1]=a[k.x][k.y+1]=0;
            else a[k.x][k.y]=a[k.x-1][k.y]=a[k.x+1][k.y]=0;
        }
        return 1;//动了
    }
    void dl()//掉落判定
    {
        for(int i=1;i<=5;i++)
        for(int j=2;j<=7;j++)
        if(a[i][j]&&!a[i][j-1])//如果一个块踩空
        for(int k=j-1;k>=0;k--)//一直往下找到一个非空块
        if(a[i][k])//找到了
        {
            swap(a[i][j],a[i][k+1]);//交换
            break;
        }
    }
    void yd(int x,int y,int k)//移动函数
    {
        swap(a[x][y],a[x+k][y]);
        if(!a[x][y])dl();//这个应该很好理解,只有和空气交换才会掉落
        while(xc())dl();//需要循环判定!!!
    }
    void dfs(int k)//搜索
    {
        if(!k)//移动完检测
        {
            if(jc())//检测
            {//输出
                for(int i=n;i>0;i--)printf("%d %d %d\n",c[i].x-1,c[i].y-1,c[i].z);
                exit(0);//直接退出程序
            }
            return;//否则返回继续搜
        }
        fz(k);//先备份a
        bool flag=0;//标记,用来做相同色优化
        for(int i=1;i<=5;i++)
        for(int j=1;j<=7;j++)
        if(a[i][j])//枚举每个点,如果有色就继续
        {
            if(i>1&&!a[i-1][j])//如果不在最左边且左边是空的就左移
            {
                yd(i,j,-1);//移动
                c[k]={i,j,-1};//记录答案
                dfs(k-1);//继续搜
                zf(k);//恢复a
            }
            if(i<5)//如果不在最右边都右移
            {
                if(a[i][j]==a[i+1][j]&&flag)continue;//已经有一个了,其余剪掉
                if(a[i][j]==a[i+1][j])flag=1;//第一个放走,然后标记
                yd(i,j,1);//移动
                c[k]={i,j,1};//记录答案
                dfs(k-1);//继续搜
                zf(k);//恢复a
            }
        }
    }
    int main()//主函数
    {
        n=read();//读n
        for(int i=1;i<=5;i++)a[i][0]=2147483647;//这个在掉落判定中有用(10以上的数都可以)
        for(int i=1;i<=5;i++)
        for(int j=1;j<=8;j++)
        {//读入
            a[i][j]=read();
            if(!a[i][j])break;
        }
        dfs(n);//搜索,n是层数(可能大部分人会写1)
        cout<<-1;//无解输出-1,因为如果有解在dfs里就退出了
        return 0;
    }
    

    欢迎 挑刺与找茬 指出错误

    • 1

    信息

    ID
    311
    时间
    3000ms
    内存
    128MiB
    难度
    5
    标签
    递交数
    0
    已通过
    0
    上传者