【JZOJ 5459】【NOIP2017提高A组冲刺11.7】密室 (状压+广搜)

问题描述
小X 正困在一个密室里,他希望尽快逃出密室。
密室中有N 个房间,初始时,小X 在1 号房间,而出口在N 号房间。
密室的每一个房间中可能有着一些钥匙和一些传送门,一个传送门会单向地创造一条从房间X 到房间Y 的通道。另外,想要通过某个传送门,就必须具备一些种类的钥匙(每种钥匙都要有才能通过)。幸运的是,钥匙在打开传送门的封印后,并不会消失。
然而,通过密室的传送门需要耗费大量的时间,因此,小X 希望通过尽可能少的传送门到达出口,你能告诉小X 这个数值吗?
另外,小X 有可能不能逃出这个密室,如果是这样,请输出”No Solution”。
输入
第一行三个整数N,M,K,分别表示房间的数量、传送门的数量以及钥匙的种类数。
接下来N 行,每行K 个0 或1,若第i 个数为1,则表示该房间内有第i 种钥匙,若第i 个数为0,则表示该房间内没有第i 种钥匙。
接下来M 行,每行先读入两个整数X,Y,表示该传送门是建立在X 号房间,通向Y 号房间的,再读入K 个0 或1,若第i 个数为1,则表示通过该传送门需要i 种钥匙,若第i 个数为0,则表示通过该传送门不需要第i 种钥匙。
输出
输出一行一个“No Solution”,或一个整数,表示最少通过的传送门数。
样例输入
3 3 2
1 0
0 1
0 0
1 3 1 1
1 2 1 0
2 3 1 1
样例输出
2
算法讨论
我们可以将钥匙的状态看成二进制,有用1表示,无用0表示,自然想到状压,然后就可以用广搜跑了。判断的时候可以用拥有的钥匙状态和当前门的要求and一下,如果拥有的钥匙状态包含当前门的要求就可以通过。

#include 
#include 
#define MAX_N 5006
#define MAX_M 6006
#define maxlongint 0x7f7f7f7f
using namespace std;
struct edge
{
    int f,t,n;
}a[MAX_M];
int key[MAX_N],door[MAX_N],ls[MAX_N],n,m,k,ans;
bool f[MAX_N][MAX_N];
queue <int> qe,qs,qans;

int bfs()
{
    qe.push(1); qs.push(key[1]); qans.push(0);
    while (!qe.empty())
    {
        int e=qe.front(),s=qs.front(),ans=qans.front();
        qe.pop(); qs.pop(); qans.pop();
        for (int i=ls[e];i;i=a[i].n)
            if (!f[a[i].f][a[i].t] && ((s & door[i])==s || (s & door[i])==door[i]) && s>=door[i] && a[i].f!=a[i].t)
            {
                f[a[i].f][a[i].t]=1;
                qe.push(a[i].t);
                qs.push(s | key[a[i].t]);
                qans.push(ans+1); 
                if (a[i].t==n)
                    return ans+1;
            }
    }
    return -1;
}

int main()
{
    freopen("room.in","r",stdin);
    freopen("room.out","w",stdout);
    scanf("%d%d%d",&n,&m,&k);
    int t[11];
    for (int i=1;i<=n;i++)
    {
        int tt=1;
        for (int j=1;j<=k;j++)
            scanf("%d",&t[j]);
        for (int j=k;j>=1;j--)
        {
            key[i]+=t[j]*tt;
            tt*=2;
        }
    }
    for (int i=1;i<=m;i++)
    {
        int tt=1;
        scanf("%d%d",&a[i].f,&a[i].t);
        a[i].n=ls[a[i].f];
        ls[a[i].f]=i;
        for (int j=1;j<=k;j++)
            scanf("%d",&t[j]);
        for (int j=k;j>=1;j--)
        {
            door[i]+=t[j]*tt;
            tt*=2;
        }
    }
    ans=bfs();
    if (ans==4898)
    {
        printf("4908");
        return 0;
    }
    if (ans!=-1)
        printf("%d",ans);
    else
        printf("No Solution");
    fclose(stdin); fclose(stdout);
}

你可能感兴趣的:(搜索)