ZOJ 3391 Haunted Graveyard(最短路负权回路)题解
题意:好长...从(0,0)走到(w-1,h-1),墓碑不能走,走到传送门只能进去不能走到其他地方,经过传送门时间会变化w(可能为负),其他地方都能上下左右走。如果能无限返老还童输出Never,走不到终点输出Impossible,其他输出最短时间。
思路:没想到是最短路,刚看懂题目还以为是暴搜+剪枝,听到无限返老还童是负权回路才想起来可以用最短路spfa来做。这题就是建边跑spfa就行了,建边方法如题意。注意一下建边的时候终点不能为边的起始,这里WA了,因为到终点了就结束了,如果还能走可能会进入负权回路。题目输出的优先级应该是这样的:有never先输出,其次最短路,最后impossible。
代码:
#include<set> #include<map> #include<cstdio> #include<utility> #include<cmath> #include<stack> #include<vector> #include<queue> #include<cstring> #include<string> #include<sstream> #include<iostream> #include<algorithm> #define ll long long #define ull unsigned long long using namespace std; const int maxn = 900+5; const int seed = 131; const int MOD = 100013; const int INF = 0x3f3f3f3f; struct Edge{ int to,val; Edge(int _to = 0,int _val = 0):to(_to),val(_val){} }; vector<Edge> G[maxn]; int w,h,turn[4][2] = {0,1,0,-1,1,0,-1,0}; set<int> grave,bh; int point(int x,int y){ return (x - 1) * h + y; } void addEdge(int u,int v,int val){ G[u].push_back(Edge(v,val)); } bool vis[maxn]; int cnt[maxn],dist[maxn]; bool spfa(int start,int n){ memset(vis,false,sizeof(vis)); for(int i = 0;i <= n;i++) dist[i] = INF; vis[start] = true; dist[start] = 0; queue<int> q; while(!q.empty()) q.pop(); q.push(start); memset(cnt,0,sizeof(cnt)); cnt[start] = 1; while(!q.empty()){ int u = q.front(); q.pop(); vis[u] = false; for(int i = 0;i < G[u].size();i++){ int v = G[u][i].to; if(dist[v] > dist[u] + G[u][i].val){ dist[v] = dist[u] + G[u][i].val; if(!vis[v]){ vis[v] = true; q.push(v); if(++cnt[v] > n) return false; } } } } return true; } int main(){ int g,e; while(scanf("%d%d",&w,&h) != EOF && w + h){ grave.clear(); bh.clear(); for(int i = 0;i <= point(w,h);i++) G[i].clear(); scanf("%d",&g); while(g--){ int x,y; scanf("%d%d",&x,&y); x++,y++; grave.insert(point(x,y)); } scanf("%d",&e); while(e--){ int x1,y1,x2,y2,w; scanf("%d%d%d%d%d",&x1,&y1,&x2,&y2,&w); x1++,y1++,x2++,y2++; addEdge(point(x1,y1),point(x2,y2),w); bh.insert(point(x1,y1)); } for(int i = 1;i <= w;i++){ for(int j = 1;j <= h;j++){ int now = point(i,j); if(i == w && j == h) continue; if(grave.count(now)) continue; if(bh.count(now)) continue; for(int k = 0;k < 4;k++){ int fx = i + turn[k][0],fy = j + turn[k][1]; if(fx < 1 || fx > w || fy < 1 || fy > h) continue; int to = point(fx,fy); if(grave.count(to)) continue; else{ addEdge(now,to,1); } } } } bool never; never = spfa(point(1,1),point(w,h)); if(!never){ printf("Never\n"); } else{ if(dist[point(w,h)] >= INF) printf("Impossible\n"); else printf("%d\n",dist[point(w,h)]); } } return 0; }