#bzoj2934#【重庆市NOIP模拟赛】业务(SPFA / Dijk)

2934: 【重庆市NOIP模拟赛】业务

时间限制:1 Sec  内存限制: 128 MB

题目描述

Mr_H 谋得一份兼职——货车司机,从此以后他将会开着货车穿行在 C 国的各大城市之间。
C 国中有 n 座城市(编号为 1~n),并且有 m 条双向公路,每条公路连接两座不同的城市。货车
从任意一座城市出发都可以抵达任意另一座城市。在每条公路上,都有一个收费站,通过的车辆需要
交纳一定过路费。可能有多条公路连接相同的两座城市。
为了增加财政收入,C 国还在每座城市也设置了收费站。并且规定,车辆从一座城市到另一座城
市的费用是,所经过公路费用和,加上所经过的城市中费用的次大值
...(这里的次大可以和最大相同,
但是城市不同)。
(这里的次大可以和最大相同,
但是城市不同)。
现在 Mr_H 告诉你今年 k 次业务运送货物的起点、终点城市列表,请你帮忙计算,每次业务需要
交纳的最低过路费。

输入

第一行包含三个用一个空格隔开的整数:n,m,k。其意义如题目描述。
第 2 到第 n+1 行:第 i+1 行包含一个单独的整数 c(1<=c<=100000),表示城市 i 的费用。
接下来的 m 行,每行包含三个整数 a,b,w,表示一条公路连接城市 a 和城市 b(1<=a,b<=n),
其过路费为 w(1<=w<=100000)。
最后的 k 行,每行包含两个整数:s,t,表示一次业务的起点和终点(1<=s,t<=n 且 s!=t)。

输出

共 k 行,每行一个整数,表示从城市 s 到 t 的最少过路费。

样例输入

5 7 3
2
5
3
3
4
1 2 3
1 3 2
2 5 3
5 3 1
5 4 1
2 4 3
3 4 4
1 3
1 4
2 3

样例输出

4
7
8

提示


对于每个i到j,假设它们中间需要经过的城市中的次大城市是k

于是可以将i到j的路程分成两部分,从i到k和从k到j

那么可以知道将会产生两种情况(注意不可以并列,直接数出第二个):

一:i到k中k为次大城市 + j到k中k为最大城市

二:i到k中k为最大城市 + j到k中k为次大城市

定义Dis[i][0]为k是次大城市的最短路

Dis[i][1]为k是最大城市的最短路

Dis[i][0] = Dis[j][0] + len[i][j] (P[k] >= P[i])

Dis[i][1] = Dis[j][1] + len[i][j] (P[k] >= P[i])

Dis[i][0] = Dis[i][1] + len[i][j] (P[k] < P[i])

注意,有人问了一个问题,我觉得挺有价值的。

下方给出的代码中,Dis[k][0]清的0,但是


Code:

#include<iostream>//枚举被作为次大值的城市k,直接强行处理出从k到所有点的距离,然后更新Cost[i][j] 
#include<cstdio>//Dis[i]表示从S到i的道路费用总和的最小值 
#include<cstdlib>
#include<cstring> 
#include<algorithm> 
#include<queue> 
using namespace std; 
  
const int Maxn = 250; 
const int INF = 0x3f3f3f3f; 
  
struct node{ 
    int v, cap, nxt; 
}edge[10005 << 1]; 
  
int N, M, K; 
int cnt; 
bool Inq[Maxn + 5]; 
int P[Maxn + 5], fir[Maxn + 5]; 
int Cost[Maxn + 5][Maxn + 5], Dis[Maxn + 5][2]; 
  
bool getint(int & num){ 
    char c; int flg = 1;    num = 0; 
    while((c = getchar()) < '0' || c > '9'){ 
        if(c == '-')    flg = -1; 
    } 
    while(c >= '0' && c <= '9' ){ 
        num = num * 10 + c - 48; 
        c = getchar(); 
    } 
    num *= flg; 
    return 1; 
} 
  
void addedge(int a, int b, int c){ 
    edge[++ cnt].v = b, edge[cnt].cap = c, edge[cnt].nxt = fir[a], fir[a] = cnt; 
    edge[++ cnt].v = a, edge[cnt].cap = c, edge[cnt].nxt = fir[b], fir[b] = cnt; 
} 
  
queue<int>Q; 
void SPFA(int S){ 
    memset(Dis, 0x3f, sizeof Dis ); 
    Dis[S][0] = Dis[S][1] = 0; 
    Q.push(S); 
    while(! Q.empty()){ 
        int tmp = Q.front();    Q.pop(); 
        Inq[tmp] = 0; 
        for(int i = fir[tmp]; i; i = edge[i].nxt){ 
            int v = edge[i].v; 
            if(P[v] <= P[S]){ 
                if(Dis[v][1] > Dis[tmp][1] + edge[i].cap){ 
                    Dis[v][1] = Dis[tmp][1] + edge[i].cap; 
                    if(! Inq[v])    Inq[v] = 1, Q.push(v); 
                } 
                if(Dis[v][0] > Dis[tmp][0] + edge[i].cap){ 
                    Dis[v][0] = Dis[tmp][0] + edge[i].cap; 
                    if(! Inq[v])    Inq[v] = 1, Q.push(v); 
                } 
            } 
            else { 
                if(Dis[v][0] > Dis[tmp][1] + edge[i].cap){ 
                    Dis[v][0] = Dis[tmp][1] + edge[i].cap; 
                    if(! Inq[v])    Inq[v] = 1, Q.push(v); 
                } 
            } 
        } 
    } 
} 
  
int main(){ 
    //freopen("business.in", "r", stdin); 
    //freopen("business.out", "w", stdout); 
    getint(N), getint(M), getint(K); 
    for(int i = 1; i <= N; ++ i)    getint(P[i]); 
    int a, b, c; 
    for(int i = 1; i <= M; ++ i){ 
            getint(a), getint(b), getint(c); 
            addedge(a, b, c); 
    } 
    memset(Cost, 0x3f, sizeof Cost ); 
    for(int k = 1; k <= N; ++ k){ 
        SPFA(k); 
        for(int i = 1; i <= N; ++ i) 
            for(int j = i + 1; j <= N; ++ j) 
                Cost[i][j] = Cost[j][i] = min(Cost[i][j], min(Dis[i][0] + Dis[j][1], Dis[i][1] + Dis[j][0]) + P[k]); 
    } 
    for(int i = 1; i <= K; ++ i) 
        getint(a), getint(b), 
        printf("%d\n", Cost[a][b]); 
    return 0; 
} 










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值