1. 程式人生 > >【JZOJ 5459】密室【最短路】

【JZOJ 5459】密室【最短路】

題目大意:

思路:

終於找到一道SPFA的變形的題目了。。。
這道題在普通SPFA的基礎上增加了條件,如果能處理好這些條件,那麼就是一個裸的SPFA
我們可以用狀態壓縮儲存每個點有的鑰匙和每條路需要的鑰匙,每條路需要的鑰匙可以直接在結構體裡面加上一維e[i].key
然後再跑SPFA的時候,再加上一個佇列keynumkeynum的每一位應該和q的每一位兩兩對應。keynum儲存的是到達這個狀態的時候的鑰匙壓縮後的數字
那麼當我們決定要走這條路的時候,除了SPFA本身的距離判斷,還要再加上一個現在擁有的鑰匙是否可以走這條路

的判斷。我們知道,如果可以走這條路,那麼就必須擁有開啟這條路的鑰匙,所以就有num&e[i].key=e[i].key
最後列舉到達終點時的鑰匙,並輸出最小值即可。

程式碼:

#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; }