1 条题解

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

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar niiick
    **

    搬运于2025-08-24 21:39:45,当前版本为作者最后更新于2018-02-20 21:33:08,作者可能在搬运后再次修改,您可在原文处查看最新版

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

    以下是正文


    关灯问题——状态压缩经典 所谓状态压缩

    就是将问题可能遇到的每一个状态用一个唯一的二进制数表示

    其复杂度一般都是指数级的 这也注定了状压类的题数据规模都不会太大


    此题中我们以1表示开灯状态,0表示关灯状态

    这样我们可以以一个长度为n的二进制数唯一的表示每个状态

    接着就可以依靠既定的开关关系将每个状态连接起来

    即将隐式图转化为显式图通过BFS最短路解决

    以灯全开状态为起点

    二进制为n个1 ,十进制表示为(1<< n)-1

    开始枚举m个开关以得到接下来的m个状态

    for(int i=1;i<=m;i++)
    {
        ss=(1<< n)-1;
        for(int j=1;j<=n;j++)
        {
            if( a[i][j]==1 && (ss&(1<<j-1)) ) ss^=(1<<j-1);
            else if( a[i][j]==-1 && !(ss&(1<<j-1)) ) ss|=(1<<j-1);
        }      
    }
    

    ss&(1<< j-1)此运算用以检查当前状态的第j位是否为1

    1<< j-1意思是将1左移j-1位,移动后只有第j位上是1

    若开关操作为1且当前状态第j位是1

    ss^=(1<< j-1) 表示 1与1异或后得0

    若开关操作为-1且当前状态第j位是0

    ss|=(1 << j-1) 表示 1或0 后得1 (此处异或操作也对,因为1异或0得1

    之后便用相同的方法在得到的每个状态上再不断搜索每个状态

    由于是BFS,所以一旦某一步状态表示为0(十进制二进制的0写法都是0)

    则当前步数必定是最短操作数

    若遍历完所有状态都没有0,则输出-1

    记得搜索过的状态要打vis标记避免重复访问


    #include<iostream>
    #include<vector>
    #include<algorithm>
    #include<queue>
    #include<cstring>
    #include<cstdio>
    using namespace std;
    
    int read()
    {
        int f=1,x=0;
        char ss=getchar();
        while(ss<'0'||ss>'9'){if(ss=='-')f=-1;ss=getchar();}
        while(ss>='0'&&ss<='9'){x=x*10+ss-'0';ss=getchar();}
        return f*x;
    }
    
    void print(int x)
    {
        if(x<0){putchar('-');x=-x;}
        if(x>9)print(x/10);
        putchar(x%10+'0');
    }
    
    int n,m;
    int a[110][1010];
    struct node{int s,step;};//s存储状态,step存储当前步数
    bool vis[1000010];
    
    int spfa()
    {
        int ss;
        queue<node> q;
        q.push( (node) {(1<<n)-1,0} );
        vis[(1<<n)-1]=true;//以全开为初始状态,二进制为n个1,十进制如代码
        
        while(!q.empty())
        {
            node u=q.front();
            q.pop();
            if(u.s==0){return u.step;}//若状态为0,则返回当前步数
            
            for(int i=1;i<=m;i++)
            {
                ss=u.s;
                for(int j=1;j<=n;j++)//由于开关对所有灯都影响,所以每个灯都遍历
                {
                    if( a[i][j]==1 && (ss&(1<<j-1)) ) ss^=(1<<j-1);
                    else if( a[i][j]==-1 && !(ss&(1<<j-1)) ) ss|=(1<<j-1);
                }//位运算解释如上文本
                
                if(!vis[ss])//若该状态未访问,就加入队列
                {
                    q.push( (node) {ss,u.step+1} );
                    vis[ss]=true;
                }
            }
        }
        return -1;
    }
    
    int main()
    {
        n=read();m=read();
        for(int i=1;i<=m;i++)
        for(int j=1;j<=n;j++)
        a[i][j]=read();//保存每个开关操作对灯的影响
        
        print(spfa());
        return 0;
    }
    
    • 1

    信息

    ID
    1663
    时间
    1000ms
    内存
    128MiB
    难度
    3
    标签
    递交数
    0
    已通过
    0
    上传者