[BZOJ2934]业务

题目描述

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


提示


题解:
ans[i][j]: 从城市i到城市j的最小费用
dis[i][0]: 从城市k出发, 到i点, cost[k]为 最大的城市费用时, 的最小费用
dis[i][1]: 从城市k出发, 到i点, cost[k]为 次大的城市费用时, 的最小费用
先通过枚举点k算出最小费用,再算出ans的值

ans的状态转移方程很容易想,当 cost[k]为次大的城市费用时:
ans[i][j]=min( dis[i][0]+dis[j][1], dis[i][1]+dis[j][0] )+cost[k]

dis要分情况讨论:
dis[ edge[i].e ][0]=min{ dis[s][0]+edge[i].w } ( cost[ edge[i].e ]<=cost[k] )
dis[ edge[i].e ][1]=min{ dis[s][0]+edge[i].w } ( cost[ edge[i].e ]>cost[k] )
dis[ edge[i].e ][1]=min{ dis[s][1]+edge[i].w }  ( cost[  edge[i].e  ]<=cost[k] )



#include<iostream>
#include<cstring>
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<queue>
using namespace std;
const int INF=0x3f3f3f3f;
const int N=255;
const int M=1e4+10;
int n, m, k, cost[N];


int fir[N], ecnt;
struct node{ int e, w, next; }edge[M<<1];
void Link( int s, int e, int w ) {
	edge[++ecnt].e=e; edge[ecnt].w=w;
	edge[ecnt].next=fir[s]; fir[s]=ecnt;
	edge[++ecnt].e=s; edge[ecnt].w=w;
	edge[ecnt].next=fir[e]; fir[e]=ecnt;
}


queue< int >q;
int dis[N][2];
bool used[N];
void Spfa( int p ) {
	memset( used, 0, sizeof used );
	memset( dis, 0x3f, sizeof dis );
	dis[p][0]=dis[p][1]=0; q.push(p);
	
	while( !q.empty() ) {
		int s=q.front(); used[s]=0; q.pop();
		for( int i=fir[s]; i; i=edge[i].next ) {
			
			if( cost[ edge[i].e ]>cost[p] ) {
				if( dis[ edge[i].e ][1]>dis[s][0]+edge[i].w ) {
					dis[ edge[i].e ][1]=dis[s][0]+edge[i].w;
					if( !used[ edge[i].e ] )
					q.push( edge[i].e ), used[ edge[i].e ]=1;
				}
			}
			else {
				if( dis[ edge[i].e ][1]>dis[s][1]+edge[i].w ) {
					dis[ edge[i].e ][1]=dis[s][1]+edge[i].w;
					if( !used[ edge[i].e ] )
					q.push( edge[i].e ), used[ edge[i].e ]=1;
				}
				
				if( dis[ edge[i].e ][0]>dis[s][0]+edge[i].w ) {
					dis[ edge[i].e ][0]=dis[s][0]+edge[i].w;
					if( !used[ edge[i].e ] )
					q.push( edge[i].e ), used[ edge[i].e ]=1;
				}
			}
			
		}
	}
}


int ans[N][N], s, e, w;
int main() {
	scanf( "%d%d%d", &n, &m, &k );
	for( int i=1; i<=n; i++ ) scanf( "%d", &cost[i] );
	for( int i=1; i<=m; i++ )
		scanf( "%d%d%d", &s, &e, &w ),
		Link( s, e, w );
	
	memset( ans, 0x3f, sizeof ans );
	for( int p=1; p<=n; p++ ) {//枚举次小点
		Spfa(p);
		for( int i=1; i<=n; i++ )
			for( int j=1; j<=n; j++ )
				ans[i][j]=min( ans[i][j], min( dis[i][0]+dis[j][1], dis[i][1]+dis[j][0] )+cost[p] );
	}
	
	for( int i=1; i<=k; i++ ) {
		scanf( "%d%d", &s, &e );
		printf( "%d\n", ans[s][e] );
	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值