小 X的密室

【题目背景】
小 X正困在一个密室里,他希望尽快逃出。
【题目描述】
密室中有 N个房间,初始时小 X在 1号房间,而出口在 号房间,而出口在 N号房间。
密室的每一个房间中可能有着些钥匙和传送门,会 密室的每一个房间中可能有着些钥匙和传送门,会 单向地 单向地 创造一条从房间 X到房间 Y的通道。另外,想要过某个传送门就必须具备一 的通道。另外,想要过某个传送门就必须具备一 的通道。另外,想要过某个传送门就必须具备一 的通道。另外,想要过某个传送门就必须具备一 的通道。另外,想要过某个传送门就必须具备一 些种类的钥匙。
幸运是,在打开传送门封印后并不会消失然而,通过密室的传送门需要耗费大量时间因此小 然而,通过密室的传送门需要耗费大量时间因此小 然而,通过密室的传送门需要耗费大量时间因此小 然而,通过密室的传送门需要耗费大量时间因此小 X希望通过尽可能 少的传送门到达出口,你能告诉小 X这个数值吗?
另外 ,小 X有可能不逃出这个密室 ,如果是这样 ,请输出 “No Solution”。
【输入格式】
从文件 room.in中读取数据。 中读取数据。
第一行三个整数 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”,或一个整数 ,表示最少通过的传送门数 表示最少通过的传送门数 。
【样例 1输入】
3 3 2
1 0
0 1
0 0
1 3 1 1
1 2 1 0
2 3 1 1
【样例 1输出】
2


将目前已拥有的钥匙压缩为一个二进制数进行转移,状态有当前的钥匙,当前位于那个房间,当前的步数,由于这题的拓扑序不确定,所以不能用dp转移,可以用图论算法,但又因为每条边的权值都为1,因此可以用直接BFS转移,复杂度为O(n*2^k)

BFS(100分)

#include<iostream>
#include<cstdio>
#include<cstring>
#define f(i,l,r) for(i=(l);i<=(r);i++)
using namespace std;
const int MAXN=50005,MAXM=60005;
struct Edge{
	int v,next,w;
}e[MAXM<<1];
struct Node{
	int sta,id;
};
int n,m,K;
int head[MAXN],tot,yao[MAXN];
int q[2][MAXN],vis[MAXN];
inline void add(int u,int v,int w)
{
	e[tot].v=v;
	e[tot].w=w;
	e[tot].next=head[u];
	head[u]=tot++;
}
namespace task1{
	void solve()
	{
		int q[MAXN],vis[MAXN];
		int he=0,tail=0,i,u,v;
		f(i,1,m){
			scanf("%d%d",&u,&v);
			add(u,v,1);
			add(v,u,1);
		}
		q[0]=1;
		while(he<=tail){
			int u=q[he++];
	//		if(he==n+1) he=1;
			for(i=head[u];~i;i=e[i].next){
				int v=e[i].v;
				if(vis[v]) continue;
				vis[v]=vis[u]+1;
		//		if(tail>n) tail=1;
				q[++tail]=v;
				if(v==n){
					he=tail;
					break;
				}	
			}
		}
		if(!vis[n]){
			printf("No Solution\n");
		}
		else{
			printf("%d\n",vis[n]);
		}
		return;
	}
}
int main()
{
//	freopen("room.in","r",stdin);
//	freopen("room.out","w",stdout);
	int i,j,u,v,w;
	memset(head,-1,sizeof(head));
	scanf("%d%d%d",&n,&m,&K);
	if(!K) task1::solve();
	else{
		f(i,1,n){
			f(j,0,K-1){
				int pd;
				scanf("%d",&pd);
				if(pd){
					yao[i]|=1<<j;
				}
			}
		}
		f(i,1,m){
			scanf("%d%d",&u,&v);
			w=0;
			f(j,0,K-1){
				int pd;
				scanf("%d",&pd);
				if(pd) w|=1<<j;
			}
			add(u,v,w);
			add(v,u,w);
		}
		int he=0,tail=0;
		q[0][0]=yao[1];
		q[1][0]=1;
		while(he<=tail){
			int u=q[1][he],sta=q[0][he++];
//			if(he==n+1) he=1;
			for(i=head[u];~i;i=e[i].next){
				int v=e[i].v;
//				cout<<u<<' '<<v<<' '<<sta<<' '<<e[i].w<<"gg"<<endl;
				if((sta&e[i].w)<e[i].w) continue;
				if(vis[v]) continue;
				vis[v]=vis[u]+1;
		//		if(tail>n) tail=1;
				q[1][++tail]=v;
				q[0][tail]=sta|yao[v];
	//			cout<<v<<endl;
				if(v==n){
					he=tail+1;
					break;
				}	
			}
		}
		if(!vis[n]){
			printf("No Solution\n");
		}
		else{
			printf("%d\n",vis[n]);
		}
	}
	return 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值