【JZOJ 5459】密室【最短路】

题目大意:

题目链接:https://jzoj.net/senior/#main/show/5459
题目图片:
http://wx1.sinaimg.cn/mw690/0060lm7Tly1fva78gse8vj30jc0fu3z6.jpg
http://wx2.sinaimg.cn/mw690/0060lm7Tly1fva78grjjuj30jb072t8m.jpg
http://wx1.sinaimg.cn/mw690/0060lm7Tly1fva78gsbd8j30hk0c1aa8.jpg
给出一个有向图,走一条边需要一些钥匙,只有拥有这些钥匙才可以走,走完后钥匙不会消失。到达点可以获得在这个点上的钥匙。求从11nn的最短路。(边权均为1)


思路:

终于找到一道SPFASPFA的变形的题目了。。。
这道题在普通SPFASPFA的基础上增加了条件,如果能处理好这些条件,那么就是一个裸的SPFASPFA
我们可以用状态压缩储存每个点有的钥匙和每条路需要的钥匙,每条路需要的钥匙可以直接在结构体里面加上一维e[i].keye[i].key
然后再跑SPFASPFA的时候,再加上一个队列keynumkeynumkeynumkeynum的每一位应该和qq的每一位两两对应。keynumkeynum储存的是到达这个状态的时候的钥匙压缩后的数字
那么当我们决定要走这条路的时候,除了SPFASPFA本身的距离判断,还要再加上一个现在拥有的钥匙是否可以走这条路的判断。我们知道,如果可以走这条路,那么就必须拥有开启这条路的钥匙,所以就有num&e[i].key=e[i].keynum\&e[i].key=e[i].k ey
最后枚举到达终点时的钥匙,并输出最小值即可。


代码:

#include <cstdio>
#include <queue>
#include <cstring>
#include <map>
#define N 6100
#define Inf 1e9
using namespace std;

int n,m,k,tot,key[N],head[N],dis[N][1024],x,y,z;
bool vis[N][1024];

struct edge
{
	int next,to,key;
}e[N];

void add(int from,int to,int num)
{
	tot++;
	e[tot].to=to;
	e[tot].key=num;
	e[tot].next=head[from];
	head[from]=tot;
}

void spfa()
{
	memset(vis,0,sizeof(vis));
	memset(dis,0x3f3f3f3f,sizeof(dis));
	vis[1][key[1]]=1;
	dis[1][key[1]]=0;  //第二维表示到达这个点时钥匙压缩后的数字
	queue<int> q;
	queue<int> keynum;
	q.push(1);
	keynum.push(key[1]);
	while (q.size())
	{
		int num=keynum.front();
		keynum.pop();
		int u=q.front();
		q.pop();
		vis[u][num]=0;
		for (int i=head[u];~i;i=e[i].next)
		{
			int v=e[i].to;
			if (((num&e[i].key)==e[i].key)&&(dis[v][num|key[v]]>dis[u][num]+1))  //num|key[v]是到达v点之后的钥匙数量
			{
				dis[v][num|key[v]]=dis[u][num]+1;
				if (!vis[v][num|key[v]])
				{
					vis[v][num|key[v]]=1;
					q.push(v);
					keynum.push(num|key[v]);
				}
			}
		}
	}
}

int main()
{
	memset(head,-1,sizeof(head));
	scanf("%d%d%d",&n,&m,&k);
	for (int i=1;i<=n;i++)
	 for (int j=k;j>=1;j--)
	 {
	 	scanf("%d",&x);
	 	if (x) key[i]+=(1<<(j-1));
	 }
	for (int i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);
		int num=0;
		for (int j=k;j>=1;j--)
		{
			scanf("%d",&z);
			if (z) num+=(1<<(j-1));
		} 
		add(x,y,num);
	}
	spfa();
	int ans=Inf;
	for (int i=0;i<=1023;i++)
	 ans=min(ans,dis[n][i]);
	if (ans<Inf) printf("%d\n",ans);
	 else printf("No Solution");
	return 0;
}
posted @ 2018-09-15 14:42  全OI最菜  阅读(73)  评论(0编辑  收藏  举报