你是ACM吗?

Description

随着中国经济的腾飞,中国的物流产业迎来了发展的春天。特别是在上海这样一个拥有广阔国内腹地的国际化大都市,物流业以空前的速度膨胀。
当然是大蛋糕就会吸引许多馋嘴猫,馋嘴猫多了就会有残酷的竞争。当大量资金流入物流产业时,KOP 集团为了稳坐在国内物流业的第一把交椅,决定对现行的运输方案进行改良,以减少自己的成本同时使其它竞争者知难而退。
作为世界100强的KOP集团当然知道要找到最优运输方案,肯定得靠数学和算法很好的软件工程师,于是他们理所当然地找到华东师范大学软件学院。决定通过赞助一场程序比赛来找出最优秀的工程师( ACM : Ace Coding Man )。
比赛只有一道题目,是运输线路的简单抽象,题意如下:
SH 市有N个运输中转点(简单标示为 1,2,3,....,N),中转点之间可能有一条运输线路,这条线路有一个特殊的地方就是从A 到B点需要耗费 c1 个单位的查克拉(SH市的货币单位),但从B到A可能需要 c2 个查克拉。当然c1不一定等于c2,也能从A到B之后就不能从B返回A了。你可以理解为这些线路是“单向”的。线路总共有 M 条。每天有N-1辆车从KOP集团总部(这里假设就是标号为1的中转点),出发,分别发往N-1个剩下的中转点,然后当天再从所到达的中转点返回。你的任务就是要求出一天的最小耗费。

Input

第一行为 C ,表示有C个测试用列。接下来分别为C个测试用列。
每个测试用例的格式如下:
第一行为两个整数,N,M ,分别表示有N个中转点和M条道路。( 1=< N, M <=1000000 .);
紧接着的M 行每行有三个值 A B c; 分别表示从中转点A到中转点B需要耗费 c 个单位的查克拉。 ( 0<= c <= 1000000000 ).

Output

你的输出应该包括C行,每行输出一个值,对应于相应的用列的最少耗费。

Sample Input

2
2 2
1 2 13
2 1 33
4 6
1 2 10
2 1 60
1 3 20
3 4 10
2 4 5
4 1 50

Sample Output

46

210

题解:先把1到各个点的最小距离和求出来。对于所有点到1的最小距离之和,我们可以将两个点的距离反转,即u->v的cost变成v->u的cost,这样,其他所有点到1的最短距离之和就是1到其他点的最小距离之和了,复杂度从n2logn变成了nlongn.足以通过。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <vector>

using namespace std;

const int INF = 0x3fffffff;

struct Node
{
	int to;
	int cost;
	Node(){}
	Node(int a,int b)
	{
		to = a;
		cost = b;
	}
	bool operator< (Node t) const
	{
		return cost > t.cost;
	}
};

vector<Node> vec[2][1000006];
long long d[2][1000006];
bool visited[1000006];

void djistra(int n,int s,int w)
{
	memset(visited,false,sizeof(visited));
	for(int i = 1;i <= n;i++)      //一开始所有点不相邻 
	{
		d[w][i] = INF;
	}
	priority_queue<Node> q;
	q.push(Node(1,0));           //将起点入队 
	d[w][s] = 0;                 //自己到自己的距离为0 
	for(int i = 1;i < n;i++)      //找出n - 1条边 
	{
		while(!q.empty())        //每次找一条到起点的最小边 
		{
			Node p = q.top();
			q.pop();
			if(!visited[p.to])  //队列中的最小边没有访问过 
			{
				visited[p.to] = true; //访问了 
			
				for(int j = 0;j < vec[w][p.to].size();j++) //更新找到的最小边到其他点的距离 
				{
					int k = vec[w][p.to][j].to;
					if(!visited[k] && d[w][k] > d[w][p.to] + vec[w][p.to][j].cost)
					{//如果该点到原点的距离+相邻点的距离大于邻接点到原点的距离,则更新该点最小距离并且入队
					 //入队之后找找到距离原点最近的点开始更新 
						d[w][k] = d[w][p.to] + vec[w][p.to][j].cost;  //更新邻接点的最小距离 
						q.push(Node(k,d[w][k]));             //压入更新的距离,因为此点可能是距离原点最近的 
					}
				}
			    break;
		    }
		}
	}
}

int main()
{
	int T;
	cin>>T;
	int n,m;
	while(T--)
	{
		scanf("%d%d",&n,&m);
		int u,v,c;
		
		for(int i = 0;i < m;i++)
		{
			scanf("%d%d%d",&u,&v,&c);
			vec[0][u].push_back(Node(v,c));  //反转,将u->v的花费变成v->u的花费, 
			vec[1][v].push_back(Node(u,c));
		}
		
		djistra(n,1,0);
		djistra(n,1,1);
		
		long long ans = 0;
		for(int i = 2;i <= n;i++)
		{
			ans += d[0][i] + d[1][i];  //将两次最短距离加上 
		}
		
		cout<<ans<<endl;
		
		for(int i = 1;i <= n;i++)
		{
			vec[0][i].clear();
			vec[1][i].clear();
		}
	}
	
    return 0;	
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值