6493. 【GDOI2020模拟03.04】迷宫

题目描述


题解

da☆ze

人类智慧题

假如当前可能所在的集合为S,等价于以光速在每个可能的房间之间来回闪现

题目中的门是不同的,并且人物也能够看出具体是ABCD中的那扇门,但是不能通过门看到走到的房间情况,可以当成传送门

假设走入A门,根据所在具体房间的不同可能从ABCD四种门中走出,那么便可以判断出当前所在的集合

设S[i][j]表示当前从i门走入会走出门j的集合,f[s]表示可能集合为s的答案

那么当前的答案=min(max(f[S[A][A]],...,f[S[A][D]]),...,max(f[S[D][A]],...,f[S[D][D]]))+1

因为题目所求的是最坏条件,所以走进每个门都会走到最坏的集合中,答案即为最坏条件下最好的选择

发现每次只+1,所以可以直接bfs拓扑

因为答案只可能变大,如果一个max里面的四个集合都算出来了,那么便可以得到当前的答案

关于bfs能走到所有状态的证明:

设S中1的个数为层数,对于同一层必然会有一个靠近0点的状态,使得一个max内必然有一个状态在下一层

而又因为一种走法对应的四个状态无交,所以其余三个状态要么不存在要么也在下一层,因此一定可以从下一层推到上一层

会卡时间,把0点删掉变成2^(n-1)即可

code

#include <bits/stdc++.h>
#define fo(a,b,c) for (a=b; a<=c; a++)
#define fd(a,b,c) for (a=b; a>=c; a--)
#define ll long long
#define file
using namespace std;

int a[8388609][3],ls[524288],p[21],b[21][4],B[21][4],c[524288][4],s[4][4],d[524288],f[524288],n,Q,i,j,k,l,x,y,len,L,h,t;

void New(int x,int y,int z)
{
    ++len;
    a[len][0]=y;
    a[len][1]=ls[x];
    ls[x]=len;
    a[len][2]=z;
    
    ++c[y][z];
}

char gc()
{
    char ch;
    
    ch=getchar();
    while (ch<'A' || ch>'D')
    ch=getchar();
    
    return ch-'A';
}

int main()
{
    freopen("maze.in","r",stdin);
    #ifdef file
    freopen("maze.out","w",stdout);
    #endif
    
    p[1]=1;
    fo(i,2,20)
    p[i]=p[i-1]*2;
    
    scanf("%d%d",&n,&Q);L=p[n]-1;
    fo(i,1,n+n)
    {
        scanf("%d",&x);k=gc();
        scanf("%d",&y);l=gc();
        
        b[x][k]=y;B[x][k]=l;
        b[y][l]=x;B[y][l]=k;
    }
    
//  ---
    
    fo(i,1,L)
    {
        memset(s,0,sizeof(s));
        
        fo(j,1,n-1)
        if (i&p[j])
        {
            fo(k,0,3)
            s[k][B[j][k]]^=p[b[j][k]];
        }
        
        fo(j,0,3)
        {
            fo(k,0,3)
            New(s[j][k],i,j);
        }
    }
    
//  ---
    
    h=0;t=1;
    d[1]=0;f[0]=1;
    while (h<t)
    {
        for (i=ls[d[++h]]; i; i=a[i][1])
        if (!f[a[i][0]] && !(--c[a[i][0]][a[i][2]]))
        {
            f[a[i][0]]=f[d[h]]+1;
            d[++t]=a[i][0];
        }
    }
    
//  ---
    
    for (;Q;--Q)
    {
        scanf("%d",&x);
        printf("%d\n",f[x>>1]);
    }
    
    fclose(stdin);
    fclose(stdout);
    
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/gmh77/p/12432026.html