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
");
        }
        else{
            if(dist[point(w,h)] >= INF) printf("Impossible
");
            else printf("%d
",dist[point(w,h)]);
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/KirinSB/p/9504471.html