shuoj1900 小6爱夜跑

Description

自从小6学了最短路算法之后,就成了一个不折不扣的最短路理论拥护者,每次在校园里夜跑的时候,只要确定好起点和终点他就能快速算出最短的路径。然而小6却没有走过每一条路,只是对这些路径长度做了一个粗略估计,于是每条路就有了估计值与实际值的差距。小6想要知道从起点到终点,按照其中任意一条预估的最短路径跑,实际最长可能需要走过的路程。(因为同一长度的最短路可能有多个)

Input

多组输入,第一行是一个整数T,表示输入数据的组数(T≤20)。

接下来有T组数据,每组数据的第一行是四个整数N、M、S、E,分别代表图中的顶点数、边数、起点编号和终点编号。

(2N100, 1M1000, 1S,TN, ST)

之后的M行每行有四个整数u, v, a, b,代表图中编号为u的点到编号为v的点有一条双向边。

每条边有两个值a、b分别代表这条边的估计长度与实际长度。

(1u,vN, 1a,b1000, u ≠ v)

数据保证两个顶点间至多只有一条双向边相连,起点与终点间必定存在通路。

Output

对于每组输入,输出一行两个整数并换行,表示小6估算出的最短路长度以及实际最长可能需要走过的路程,两个整数间有一个空格。

Sample Input

2
2 1 1 2
1 2 3 4
3 3 1 3
1 2 2 3
1 3 3 4
2 3 1 2

Sample Output

3 4
3 5


分析:
           本题是个最短路问题,用priority_queue优化的dijkstra算法,dijkstra利用了最优子结构的思想求最短路,不过在此基础上做了演变,所求的是起点到终点的预估长度(最短路)和实际长度(“最长路”),最长路并不是完全意义上的最长路,题干中表明了实际距离是在选取了任意一个预估距离之后的其中最长距离,“最长路”即是在多条最短路中的最长路,用dis[x]表示起点到各点的最短路,res[x]表示最长路。也是在帮助之下才完全想明白,即在更新最短路dis[y]时,最长路res[y]是不受影响的,和dis[x]一样进行更新,只有直到出现了相同最短路即(dis[y]==dis[x]+d)的时候,才选取其中距离较长的更新res[y].


代码:

#include <bits/stdc++.h>
#define PB(x) push_back(x)
#define MP(a,b) make_pair(a,b)
#define INF 0x3FFFFFFF
using namespace std;
typedef pair<int,int> PII;
typedef vector<PII> VII;
typedef pair<pair<int,int>,int> PIII;
typedef vector<PIII> VIII;             //用vector存图 

const int MAXN = 100010;
VIII G[MAXN];
void add_edge(int u,int v,int d,int w){
    G[u].PB(MP(MP(v,d),w));          //表示点u到点v的预估距离为d,实际距离为w 
}
void init(int n){			//初始化清零 
    for(int i=0;i<n;i++){
        G[i].clear();
    }
}
int vis[MAXN];		//记录访问过的边 
int dis[MAXN];		//记录最短路 
int res[MAXN];		//记录最长路 
void dijkstra(int s,int n){
    for(int i=0;i<n;i++)vis[i] = 0;
    for(int i=0;i<n;i++)dis[i] = (i == s ? 0 : INF);	//每次求最短,初始值设为最大 
    for(int i=0;i<n;i++)res[i] = (i == s ? 0 : -1);		//每次求最长,初始值设为负 
    //优先队列优化,dis[s]在前保证每次距离最小的点在顶,优先更新最短距离的点,充分利用优先子结构的性质。 
	priority_queue<PII,VII,greater<PII> >q;			 
    q.push(MP(dis[s],s));      
    while(!q.empty()){
        PII p = q.top();
        int x = p.second;
        q.pop();
        if(vis[x]) continue;	//访问过的点不再访问 
        vis[x] = 1;				//取出起点x对应的所有终点y,分别更新预估距离d和实际距离w
        for(int i=0;i<G[x].size();i++){
            int y = G[x][i].first.first;	 
            int d = G[x][i].first.second;
            int w = G[x][i].second;
            if(!vis[y] && dis[x] + d < dis[y]){		//若当前最短距离能被更新,同时更新dis,res数组 
                dis[y] = dis[x] + d;
                res[y] = res[x] + w;
//              res[y] = max(res[y],res[x]+w);
//              cout<<res[y]<<endl;
                q.push(MP(dis[y],y));
            }
            if(!vis[y] && dis[x]+d==dis[y]){     //只有出现相同最短路径时,才取较长的实际距离更新res数组 
            	res[y]=max(res[y],res[x]+w);
			}
        }
    }
}
int main(int argc, char** argv) {
	int t;
	scanf("%d",&t);
	while(t--){
		int n,m,s,t;
		scanf("%d%d%d%d",&n,&m,&s,&t);
		init(n);
		while(m--){
			int u,v,d,b;
			scanf("%d%d%d%d",&u,&v,&d,&b);
			u--;v--;			//下标从0开始 
			add_edge(u,v,d,b);
			add_edge(v,u,d,b);
		}
		dijkstra(s-1,n);
		cout<<dis[t-1]<<' '<<res[t-1]<<endl;
	}
	return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值