JZOJ 6493. 【GDOI2020模擬03.04】迷宮
題解
- 這道題的題面比較玄學,乍一看還以爲是道PJ難度的最短路。。。
- 其實,題目的設定十分有趣,你被放進了迷宮裏,只能分清門(A/B/C/D),而不能分清房間(0除外),也就是說,你只知道當前可能所在的房間集合,和整個迷宮的結構,而並不知道具體在哪個房間,不過可以推算每一步可能所在的房間集合,希望最少的步數能保證走出這個迷宮(走出0號房間),
- 直接設dis[s]爲當前可能在的房間集合爲s的最終答案,然後進行轉移。
- 一種很自然的想法,每個狀態s直接由四個狀態轉移過來,分別是走四扇門出去到達的房間集合,
- dis[s]=min(dis[s1],dis[s2],dis[s3],dis[s4])+1
- 然而仔細推敲過後,發現這樣並不是正確的(答案可能更優)
- 以下是重點內容
- 我們不僅可以通過當前的房間集合和選擇走出的門判斷走到的房間集合,也可以通過走出後到達的門得出走到的房間集合,
- 簡單地說,就是原本的四種轉移(A/B/C/D)變成了十六種(A->A/A->B/A->C…D->D,分別爲走出的門和走進的另一扇門),
- 不過,也不是直接在這十六種狀態中取min更新,因爲走進的另一扇門是在走出前不能確認的嗎,也就是說,選擇好了特定的一扇門走出後,才能通過走進的門得到當前可能在的四個集合,這四個是相互獨立的,但都是在選擇好了走出的門的前提下的,
- 因此還是要枚舉選哪個門走出去,在走出去後的四種情況中取max(注意,是max),作爲轉移的值,去更新dis[s]。
- 具體實現需要分層DP,寫的不好可能會被卡。
代碼
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define N 1050000
int st[25][4][2];
int a[25],g[N][4][4];
int last[N],nxt[N*16],to[N*16][4],len=0;
int dis[N],p[N],q[N],e[N],b[21][N];
void add(int x,int A,int B,int C,int y)
{
to[++len][0]=y;
to[len][1]=A,to[len][2]=B,to[len][3]=C;
nxt[len]=last[x];
last[x]=len;
}
int main()
{
int n,Q,i,j,k,x,y;
scanf("%d%d",&n,&Q);
for(i=1;i<=2*n;i++)
{
char c,d;
scanf("%d %c %d %c",&x,&c,&y,&d);
st[x][c-'A'][0]=y,st[x][c-'A'][1]=d-'A';
st[y][d-'A'][0]=x,st[y][d-'A'][1]=c-'A';
}
st[0][0][0]=st[0][1][0]=st[0][2][0]=st[0][3][0]=0;
st[0][0][1]=0,st[0][1][1]=1,st[0][2][1]=2,st[0][3][1]=3;
for(i=0;i<4;i++)
for(j=0;j<4;j++) g[1][i][j]=1;
for(i=2;i<(1<<n);i++)
{
int t=i,s=0;
while(t%2==0) t/=2,s++;
for(j=0;j<4;j++)
for(k=0;k<4;k++) g[i][j][k]=g[i-(1<<s)][j][k];
for(j=0;j<4;j++)
g[i][j][st[s][j][1]]|=1<<st[s][j][0];
}
e[0]=0;
for(i=1;i<=1<<n;i++) if(i%2) e[i]=e[i/2]+1; else e[i]=e[i/2];
for(i=1;i<1<<n;i++) b[e[i]][++b[e[i]][0]]=i;
memset(dis,127,sizeof(dis));
dis[0]=0,dis[1]=1;
for(k=1;k<=n;k++)
{
for(i=2;i<=(1<<n);i++)
{
if(dis[i]<=k) continue;
dis[i]=min(dis[i],max(max(dis[g[i][0][0]],dis[g[i][0][1]]),max(dis[g[i][0][2]],dis[g[i][0][3]]))+1);
dis[i]=min(dis[i],max(max(dis[g[i][1][0]],dis[g[i][1][1]]),max(dis[g[i][1][2]],dis[g[i][1][3]]))+1);
dis[i]=min(dis[i],max(max(dis[g[i][2][0]],dis[g[i][2][1]]),max(dis[g[i][2][2]],dis[g[i][2][3]]))+1);
dis[i]=min(dis[i],max(max(dis[g[i][3][0]],dis[g[i][3][1]]),max(dis[g[i][3][2]],dis[g[i][3][3]]))+1);
}
}
while(Q--)
{
scanf("%d",&x);
printf("%d\n",dis[x]);
}
return 0;
}