Light1074 Extended Traffic (标记负环上的节点)

本文介绍了一种解决达卡市交通拥堵问题的算法。通过评估道路繁忙度,利用Bellman-Ford或SPFA算法寻找可能的负环,并标记这些节点以避免其使用。文章详细解释了算法流程和代码实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

问题描述

达卡市一天比一天拥挤嘈杂。某些道路总是堵在路上。为了说服人们避开最短的路线,从而避开拥挤的道路,到达目的地,市当局制定了一个新的计划。城市的每个交叉口都用一个正整数(≤20)表示交叉口的繁忙程度。每当有人从一个路口(源路口)走到另一个路口(目的地路口),城市当局就会从旅行者那里获得数量(目的地繁忙度-源繁忙度)3(即差值的立方)。权威已经指定你去找出当一个聪明的人从某个交点(零点)到其他几个交点时能挣到的最低工资总额。

输入

输入以整数T(≤50)开始,表示测试用例的数量。
每个case包含一个空行和一个整数n (1 < n≤200),表示连接的数量。下一行包含n个整数,分别表示从1到n的连接的繁忙程度。下一行包含一个整数m,即城市中的道路数量。接下来的m行(每条路一条)包含两个连接点——对应的路连接的数字(源、目的)(所有的路都是单向的)。下一行包含整数q,即查询的数量。接下来的q行各包含一个目的地连接号。从一个路口到另一个路口最多只能有一条直路。

输出

对于每种情况,在一行中打印案例号。然后打印q行,每个查询一个行,每个行包含从结1(零点)到给定结的最低总收入。但是,对于总收益小于3的查询,或者如果不能从0点到达目的地,则打印一个’?’。

Sample Input

2

5
6 7 8 9 10
6
1 2
2 3
3 4
1 5
5 4
4 5
2
4
5

2
10 10
1
1 2
1
2

Sample Output

Case 1:
3
4

Case 2:
?

分析:

第一次写标记负环节点的题,记录一下

(目的地繁忙度-源繁忙度)3显然可能会导致出现负环
可以用bellmanford或者spfa找到负环
并且在找到负环的时候DFS标记负环上的所有节点
(负环权值可以一直缩小,值显然小于3,所以输出’?’)

code:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
#define set0(a) memset(a,0,sizeof(a))
#define set1(a) memset(a,-1,sizeof(a))
#define setinf(a) memset(a,0x3f3f3f3f,sizeof(a))
#define ll long long
#define P pair<int,int>
#define mp(a,b) make_pair(a,b)
const int inf=0x3f3f3f3f;
const int inn=0x80808080;
using namespace std;
const int maxm=5e4+5;
int head[maxm],to[maxm],w[maxm],nt[maxm];
int cnt;
int a[maxm],d[maxm],mark[maxm],num[maxm];
bool cir[maxm];
void add(int x,int y,int z){
    cnt++;nt[cnt]=head[x];head[x]=cnt;to[cnt]=y;w[cnt]=z;
}
void init(){
    set1(head);
    cnt=0;
    set0(cir);
    set0(mark);
    set0(num);
    setinf(d);
}
void dfs(int x){//把负环上的点全部标记
    cir[x]=1;//标记
    for(int i=head[x];i!=-1;i=nt[i]){
        int v=to[i];
        if(!cir[v]){
            dfs(v);
        }
    }
}
void spfa(int st,int n){
    queue<int>q;
    q.push(st);
    d[st]=0;
    mark[st]=1;
    num[st]=1;
    while(!q.empty()){
        int x=q.front();
        q.pop();
        mark[x]=0;
        if(cir[x])continue;//负环跳过
        for(int i=head[x];i!=-1;i=nt[i]){
            int v=to[i];
            if(cir[v])continue;//不走负环,跳过
            if(d[v]>d[x]+w[i]){
                d[v]=d[x]+w[i];
                if(!mark[v]){
                    mark[v]=1;
                    q.push(v);
                    num[v]++;
                    if(num[v]>=n){
                        dfs(v);//出现负环就dfs标记
                    }
                }
            }
        }
    }
}
int main(){
    int T;
    int cas=1;
    scanf("%d",&T);
    while(T--){
        init();
        int n;
        scanf("%d",&n);
        for(int i=1;i<=n;i++){
            scanf("%d",&a[i]);
        }
        int m;
        scanf("%d",&m);
        while(m--){
            int x,y;
            scanf("%d%d",&x,&y);
            int temp=a[y]-a[x];
            add(x,y,temp*temp*temp);
        }
        spfa(1,n);
        int q;
        scanf("%d",&q);
        printf("Case %d:\n",cas++);
        while(q--){
            int t;
            scanf("%d",&t);
            if(cir[t]||d[t]==inf||d[t]<3){//1.负环上 2.无法到达 3.答案小于三
                printf("?\n");
            }else{
                printf("%d\n",d[t]);
            }
        }
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值