JZOJ5459. 【NOIP2017提高A组冲刺11.7】密室

7 篇文章 0 订阅

Description

小X 正困在一个密室里,他希望尽快逃出密室。
密室中有N 个房间,初始时,小X 在1 号房间,而出口在N 号房间。
密室的每一个房间中可能有着一些钥匙和一些传送门,一个传送门会单向地创造一条从房间X 到房间Y 的通道。另外,想要通过某个传送门,就必须具备一些种类的钥匙(每种钥匙都要有才能通过)。幸运的是,钥匙在打开传送门的封印后,并不会消失。
然而,通过密室的传送门需要耗费大量的时间,因此,小X 希望通过尽可能少的传送门到达出口,你能告诉小X 这个数值吗?
另外,小X 有可能不能逃出这个密室,如果是这样,请输出”No Solution”。

Input

第一行三个整数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 种钥匙。

Output

输出一行一个“No Solution”,或一个整数,表示最少通过的传送门数。

Sample Input

3 3 2
1 0
0 1
0 0
1 3 1 1
1 2 1 0
2 3 1 1

Sample Output

2

数据范围

这里写图片描述
这里写图片描述

题解

观察数据范围,
k很小,只有10,所以自然地想到状态压缩。
将每个房间,每一条边都变为一个状态。
fi,s 表示到达第i个点,所拥有的钥匙状态为s的最小步数。
转移就是类似spfa。
然而实际上并不用spfa,
因为边权都是相同的,都为1,直接bfs就好了。

code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 5003
#define P putchar
using namespace std;
void read(int &n)
{
    int t=0,p=1;char ch;
    for(ch=getchar();!('0'<=ch && ch<='9');ch=getchar())
      if(ch=='-') p=-1;
    for(;'0'<=ch && ch<='9';ch=getchar()) t=t*10+ch-'0';
    n=t*p;
}

void write(int x)
{
    if(x>9)write(x/10);
    P(x%10+48);
}

int z[13],n,m,k,x,y,nxt[N+1000],to[N+1000],b[N],s[N],v[N+1000],f[N][1024],d[N],head,tail,ss,ans;
bool bz[N];

int main()
{
    freopen("room.in","r",stdin);
    freopen("room.out","w",stdout);
    z[0]=1;
    for(int i=1;i<13;i++)
        z[i]=z[i-1]*2;

    memset(s,0,sizeof(s));
    memset(bz,1,sizeof(bz));
    memset(f,127,sizeof(f));
    memset(v,0,sizeof(v));
    read(n);read(m);read(k);
    for(int i=1;i<=n;i++)
        for(int j=0;j<k;j++)
            read(x),s[i]+=x*z[j];
    for(int i=1;i<=m;i++)
    {
        read(x);read(y);
        nxt[i]=b[x];
        to[i]=y;
        b[x]=i;
        for(int j=0;j<k;j++)
            read(x),v[i]+=x*z[j];
    }

    f[1][0]=head=bz[d[1]=tail=1]=0;

    while(head<tail)
    {
        x=d[++head];
        for(int tt=0;tt<z[k];tt++)
        {
            if(f[x][tt]>N)continue;
            ss=tt|s[x];
            for(int i=b[x];i;i=nxt[i])
                if((v[i]&ss)>=v[i])
                    if(f[x][tt]+1<f[to[i]][ss])
                    {
                        f[to[i]][ss]=f[x][tt]+1;
                        if(bz[to[i]])
                        {
                            bz[to[i]]=0;
                            d[++tail]=to[i];
                        }
                    }
        }
        bz[x]=1;
    }

    ans=2147483647;
    for(int i=0;i<z[k];i++)
        ans=ans<f[n][i]?ans:f[n][i];
    if(ans>N)printf("No Solution");else write(ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值