ZOJ--Highway Project(spfa+两级选择)

ZOJ--Highway Project(spfa+两级选择)

Highway Project

Time Limit: 2 Seconds       Memory Limit: 65536 KB

Edward, the emperor of the Marjar Empire, wants to build some bidirectional highways so that he can reach other cities from the capital as fast as possible. Thus, he proposed the highway project.

The Marjar Empire has N cities (including the capital), indexed from 0 to N - 1 (the capital is 0) and there are M highways can be built. Building the i-th highway costs Ci dollars. It takes Di minutes to travel between city Xi and Yi on the i-th highway.

Edward wants to find a construction plan with minimal total time needed to reach other cities from the capital, i.e. the sum of minimal time needed to travel from the capital to city i (1 ≤i ≤ N). Among all feasible plans, Edward wants to select the plan with minimal cost. Please help him to finish this task.

Input

There are multiple test cases. The first line of input contains an integer T indicating the number of test cases. For each test case:

The first contains two integers NM (1 ≤ NM ≤ 105).

Then followed by M lines, each line contains four integers XiYiDiCi (0 ≤ XiYi < N, 0 < DiCi < 105).

Output

For each test case, output two integers indicating the minimal total time and the minimal cost for the highway project when the total time is minimized.

Sample Input
2
4 5
0 3 1 1
0 1 1 1
0 2 10 10
2 1 1 1
2 3 1 2
4 5
0 3 1 1
0 1 1 1
0 2 10 10
2 1 2 1
2 3 1 2
Sample Output
4 3
4 4


题意:N,M (1 ≤ N, M ≤ 105).由于城市数目太多,不能用dijkstra解,因为开二维数组会超内存,只能选择spfa.

共有n个城市,编号:0......n-1,按照从0号城市出发分别到达其它所有城市总时间最小,若有多组结果则输出总花费最小的建设方案。最后输最小总时间和以及在该条件条件下,花费的最小值 .

第一个样例分析:
最终选出的3条路: 
0  1   1    1
0  3   1    1
2  1   1    1
经过更新得到:0   1    1     1

0   2    2     2
0   3    1     1

可见,总路程为4,总花费应为3而不是4.应为在计算0~~2路段的花费中,把0~~1路段的花费又计算了一次,即:0~~1的造价被重复计算了2次。


My  solution:

/*2016.4.23*/

#include <cstdio>
#include <cstring>
#include <queue>
#include<algorithm>
using namespace std;
#define M 300000
#define N 300500
#define INF 0x3f3f3f3f
using namespace std;
long  long n, m, num;
long  long head[M], dis[N], spe[N],d[N];
bool vis[N];
//d数组记录,编号0到编号为n的城市中,被重复计算的路段的造价 
struct Edge {
	long  long  form, to, val, next, time;
};
Edge edge[M];

void AddEdge(long  long a, long  long b, long  long c, long  long d) {
	Edge E = {a, b, c, head[a], d};
	edge[num] = E;
	head[a] = num++;
}
void Init() {
	num = 0;
	memset(head, -1, sizeof(head));
}

void Spfa(long  long s) {
	queue <long  long> Q;
	memset(dis, INF, sizeof(dis));
	memset(d,0,sizeof(d));
	memset(spe, INF, sizeof(spe));
	memset(vis, 0, sizeof(vis));
	Q.push(s);
	dis[s] = 0;//更新最短路 
	spe[0] = 0;//从0号城市,到n号城市的道路全部造价,包含被重复计算路段的造价 
	vis[s] = true;//标记数组 
	while(!Q.empty()) {
		long  long x = Q.front();
		Q.pop();
		vis[x] = false;
		for(long  long i = head[x]; i != -1; i = edge[i].next) {
			long  long v = edge[i].to;
			if(dis[v] > dis[x] + edge[i].val) {//当两种方案的道路长度不同时,更新最短路以及花费值 
				d[v]=spe[x];
				dis[v] = dis[x] + edge[i].val;
				spe[v] = spe[x] + edge[i].time;
				if(!vis[v]) {
					Q.push(v);
					vis[v] = true;
				}
			}
 //当两种方案的道路长度相同时,比较哪种方案的造价最低,选择造价低的方案 
//if((dis[v] == dis[x] + edge[i].val) && (spe[v] >= spe[x] + edge[i].time){
//注意在比较造价时,不是比较从0到n的总造价最低,而是比较去掉重复段后的未休路段的造价                                                                              
			if((dis[v] == dis[x] + edge[i].val) && (spe[v]-d[v] >edge[i].time)){
//spe[v]-d[v]表示第一种方案去掉重复路段后所需造价,edge[i].time(=spe[x] + edge[i].time-spe[x])第二种方案去掉重复路段后的造价 
				d[v]=spe[x];//记录,更新后的spe[v]中重复路段的造价 
				spe[v] = spe[x] + edge[i].time;
				if(!vis[v]) {
					Q.push(v);
					vis[v] = true;
				}
			}
		}
	}
	long  long t1 = 0, t2 = 0;
	for(long  long i = 1; i < n; i++) {
		t1 += dis[i];
		t2 += spe[i];
		t2-=d[i];//去掉重复路段的造价 
	}
	printf("%lld %lld\n", t1, t2);
}

int main() {
	long  long a, b, c, d;
	long  long t;
	scanf("%lld", &t);
	while(t--) {
		scanf("%lld%lld", &n, &m);
		Init();
		while(m--) {
			scanf("%lld%lld%lld%lld", &a, &b, &c, &d);
			AddEdge(a, b, c, d);
			AddEdge(b, a, c, d);
		}
		Spfa(0);
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值