题目大意:
题目链接: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
给出一个有向图,走一条边需要一些钥匙,只有拥有这些钥匙才可以走,走完后钥匙不会消失。到达点可以获得在这个点上的钥匙。求从到的最短路。(边权均为1)
思路:
终于找到一道的变形的题目了。。。
这道题在普通的基础上增加了条件,如果能处理好这些条件,那么就是一个裸的。
我们可以用状态压缩储存每个点有的钥匙和每条路需要的钥匙,每条路需要的钥匙可以直接在结构体里面加上一维。
然后再跑的时候,再加上一个队列,的每一位应该和的每一位两两对应。储存的是到达这个状态的时候的钥匙压缩后的数字。
那么当我们决定要走这条路的时候,除了本身的距离判断,还要再加上一个现在拥有的钥匙是否可以走这条路的判断。我们知道,如果可以走这条路,那么就必须拥有开启这条路的钥匙,所以就有。
最后枚举到达终点时的钥匙,并输出最小值即可。
代码:
#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;
}