正题
jzoj题目链接:https://jzoj.net/senior/#main/show/5309
题目大意
有n个点,m条边,k种钥匙。有些点分布了钥匙,有些边需要一些钥匙才可以通过,求1到n的最短路。
解题思路
将图分成2k2k层,每一层用二进制表示不同的钥匙情况。然后根据不同情况连边就好了。
(双端队列bfs会莫名其妙WA一个点)
code
#include<cstdio>
#include<queue>
#include<cstring>
#define p(x,y) x*n+y
#define N 5110
#define M 6110
#define MS 1024
using namespace std;
struct node{int to,next;bool w;
}a[MS*(N+M)];
int n,m,k,x,y,w,MAX_State,d[N*MS],tot,ls[N*MS];
bool v[N*MS];
queue<int> q;
void addl(int x,int y,int w)//加边
{a[++tot].to=y;a[tot].next=ls[x];a[tot].w=w;ls[x]=tot;
}
void bfs()//SPFA
{memset(d,127/3,sizeof(d));q.push(p(0,1));d[p(0,1)]=0;v[p(0,1)]=1;while(!q.empty()){int x=q.front();q.pop();for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(d[x]+a[i].w<d[y]){d[y]=d[x]+a[i].w;if(!v[y]){v[y]=true;q.push(y);}}}v[x]=false;}
}
int main()
{scanf("%d%d%d",&n,&m,&k);MAX_State=1<<k;for(int i=1;i<=n;i++)for(int j=1;j<=k;j++){scanf("%d",&x);if(x){for(int state=0;state<MAX_State;state++)addl(p(state,i),p((state|(1<<(j-1))),i),0);//不同情况的获取钥匙}}for(int i=1;i<=m;i++){int state=0;scanf("%d%d",&x,&y);for(int i=1;i<=k;i++){scanf("%d",&w);state+=w<<(i-1);}for(int j=0;j<MAX_State;j++)if((j&state)==state)addl(p(j,x),p(j,y),1);//满足钥匙的情况连边}bfs();int ans;ans=2147483647;for(int j=0;j<MAX_State;j++)ans=min(ans,d[p(j,n)]);//因为没有要求钥匙状态所以取最小的if(ans>10000) printf("No Solution");else printf("%d",ans);
}