题目描述
有一张n×m的网格图,格与格之间可能有门和墙,墙不可经过,门必须有相应的钥匙,求从左上角到右下角的最短路径。
思路
虽然这是网络流24题,但实际上我们可以用更简单的思路——分层图来做。我们考虑p比较小,也就是钥匙的种类比较少,可以状态压缩后有一个数表示所用的钥匙个数,我们据此可以建成一张(1<<k)层的分层图,每一层对应拥有钥匙的一种状态,所以我们可以把每一层的基本图建成,再处理一下层之间的连边,跑一遍spfa求最短路即可。
代码
#include <bits/stdc++.h> using namespace std; const int N=1e6+10,INF=100000000; struct aa { int x,y; aa(int x=0,int y=0):x(x),y(y) {} }; vector<aa>key[20]; struct Edge { int to,nxt,w; }e[N]; int head[N],tot,num[20][20],mp[200][200]; int keyn,m,n,p,l,dis[N],exist[N],k; void add_edge(int x,int y,int v) { e[++tot].nxt=head[x];head[x]=tot; e[tot].to=y;e[tot].w=v; } void build() { l=(1<<p); int s=m*n; int have_key[20]={0}; //有那几把钥匙 for(int k=0;k<l;k++) { for(int i=1;i<=p;i++) if(k&(1<<i-1))have_key[i]=1; //把状压传化过来 else have_key[i]=0; for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) { int x=num[i][j],y=num[i][j+1]; //向右走 if(y!=0&&mp[x][y]!=-1) { if(mp[x][y]==0||have_key[mp[x][y]]) { add_edge(k*s+x,k*s+y,1); add_edge(k*s+y,k*s+x,1); } } y=num[i+1][j]; //向下走 if(y!=0&&mp[x][y]!=-1) { if(mp[x][y]==0||have_key[mp[x][y]]) { add_edge(k*s+x,k*s+y,1); add_edge(k*s+y,k*s+x,1); } } } for(int i=1;i<=p;i++) if(!have_key[i]) { int goal=k+(1<<i-1); for(int j=0;j<key[i].size();j++) { int x=num[key[i][j].x][key[i][j].y]; add_edge(k*s+x,goal*s+x,0); //层之间的连边 } } } } void spfa() { queue<int>q; for(int i=1;i<=m*n*l;i++) dis[i]=INF; memset(exist,0,sizeof(exist)); dis[1]=0;q.push(1);exist[1]=1; while(!q.empty()) { int u=q.front();q.pop(); for(int i=head[u];i;i=e[i].nxt) { int v=e[i].to; if(dis[v]>dis[u]+e[i].w) { dis[v]=dis[u]+e[i].w; if(!exist[v]) { q.push(v); exist[v]=1; } } } exist[u]=0; } } int main() { int cnt=0; scanf("%d%d%d%d",&n,&m,&p,&k); for(int i=1;i<=n;i++) for(int j=1;j<=m;j++) num[i][j]=++cnt; for(int i=1;i<=k;i++) { int x1,x2,y1,y2,g; scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&g); if(g==0)g=-1; int a=num[x1][y1],b=num[x2][y2]; mp[a][b]=mp[b][a]=g; } scanf("%d",&keyn); for(int i=1;i<=keyn;i++) { int x,y,q; scanf("%d%d%d",&x,&y,&q); key[q].push_back(aa(x,y)); } /* for(int i=1;i<=p;i++) { printf("%d ",i); for(int j=0;j<key[i].size();j++) printf("%d %d ",key[i][j].x,key[i][j].y); printf(" "); }*/ build(); spfa(); int ans=INF; for(int i=0;i<l;i++) ans=min(ans,dis[i*m*n+num[n][m]]); if(ans<INF)printf("%d",ans); else printf("-1"); return 0; }