LightOJ 1074 - Extended Traffic 【SPFA】

<题目链接>

题目大意:
有n个城市,每一个城市有一个拥挤度Ai,从一个城市I到另一个城市J的时间为:(A(v)-A(u))^3。问从第一个城市到达第k个城市所花的时间,如果不能到达,或者时间小于3输出?否则输出所花的时间。

解题分析:

很明显,此题路段的权值可能为负,所以我们就不能用Dijkstra算法求最短路了。需要注意的是,当点存在负环的时候,就要将负环所能够到达的所有点全部标记,从起点到这些点的最短路是不存在的(因为假设如果存在最短路,那么只要途中在负环上多走几遍,那么重新算得的时间一定会变少,所以不存在最短路)。所以,总的来说,对于本题,对那些负环能够到达的点,和从起点无法到达的点,和时间小于3的点,全部输出“?”,其他满足条件的直接输出最短时间就行。

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;


const int MAXN=220;
const int INF =0x3f3f3f3f;

struct Edge{
    int to;
    int next;
    int val;
}edge[MAXN*MAXN];

int res,n;
int head[MAXN];
bool vis[MAXN];      //记录该点是否在队列内
bool cir[MAXN];      //记录该点是否为负环上的点
int a[MAXN],dist[MAXN],cnt[MAXN];   //cnt[]数组记录该数在队列中出现的次数

void dfs(int u){     //将该负环所能够达到的所有点全部标记
    cir[u]=true;
    for(int i=head[u];i!=-1;i=edge[i].next){
        int v=edge[i].to;
        if(!cir[v])dfs(v);
    }
}

void init(){
    memset(head,-1,sizeof(head));
    res=0;
}

void add(int u,int v,int w){
    edge[res].to=v,edge[res].val=w;
    edge[res].next=head[u];
    head[u]=res++;
}

void SPFA(int st){
    memset(vis,false,sizeof(vis));
    memset(cir,false,sizeof(cir));
    for(int i=1;i<=n;i++)
        dist[i]=INF;
    vis[st]=true;
    dist[st]=0;
    queue<int>q;
    q.push(st);
    memset(cnt,0,sizeof(cnt));
    cnt[st]=1;
    while(!q.empty()){
        int u=q.front();
        q.pop();
        vis[u]=false;   //当该点从队列中pop掉之后,就要清除vis标记 
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].to;
            if(cir[v])continue;  //如果是负环上的点
            if(dist[v]>dist[u]+edge[i].val){
                dist[v]=dist[u]+edge[i].val;
                if(!vis[v]){    //如果该点不在队列中
                    vis[v]=true;
                    q.push(v);
                    cnt[v]++;
                    if(cnt[v]>n){    //若存在负环,就用dfs将该负环能够达到的所有点标记
                        dfs(v);
                    }
                }
            }
        }
    }
}

int main(){
    int t;scanf("%d",&t);
    int ncase=0;
    while(t--){
        init();
        scanf("%d",&n);
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        int m;
        scanf("%d",&m);
        while(m--){
            int u,v;
            scanf("%d %d",&u,&v);
            add(u,v,(a[v]-a[u])*(a[v]-a[u])*(a[v]-a[u]));
        }

        SPFA(1);
        printf("Case %d:\n",++ncase);
        scanf("%d",&m);
        while(m--){
            int u;scanf("%d",&u);
            if(cir[u] || dist[u]<3 || dist[u] == INF)  //如果询问的点能由负环达到、或者到起点的最小受益小于3、或者询问的点不可达
                printf("?\n");
            else printf("%d\n",dist[u]);
        }
    }
    return 0;
}

2018-08-31

猜你喜欢

转载自www.cnblogs.com/00isok/p/9568773.html