Silver Cow Party POJ - 3268+线性空间中多源最短路转化为单源最短路

			        		****首先,感谢pxgg一直以来的支持和帮助*****

题目链接

*题目大意,N头奶牛,从各自地点(就是他们的编号1,2,--N),沿着一条单向路,前去X(1<=X<=N)地,
*参加party,party后,N头奶牛,沿着一条单向路回到原地。问来回需要最小的时间。
数据范围:1 ≤ N ≤ 10001 ≤ M ≤ 100,000,Ti,1 ≤ Ti ≤ 100;依次为点的数目,边的数目,一条路所需时间。
时间要求:2000ms;
所用算法:Floyd,O(N^3)超时;bellman,O(N*M),超时;**dijkstra**,优化前,O(N^2,可跑,**优化**后,O((N+M)log2(N)),强。
题目要求:(1) 求出n头奶牛到一个地点的距离sum1,
		 (2) 求出n头奶牛从一个地点到n个地点的距离sum2。
		 (3) 输出sum1+sum2;
第(2)个问题容易解决,就是跑一边dijkstra,算出X点到n个点的最小距离的和。
第(1)个问题求的是n个点到1个点X的距离,我们这样想:
有4的点,1234,要去2号点,已知最短路线如下:
这些路径是单向的,如果我们求每个点到2的最短距离,最多跑n次,dijkstra复杂度也不行,肯定会超。
路径的长度是一定的,那么我们这样想,现在让牛走到2号点的时候沿着原路返回。走的这些距离就是sum1;
1-->3-->2;
23-->4-->2;
4-->1-->2;

这些路径就成了:
2-->3-->1;
2;
2-->4-->3;
2-->1-->4;

我们的问题又变成了求2号点到各个顶点的距离。
***但是要把所有的边都逆向处理,因为如果不处理的话,怎么求啊,哈哈哈哈哈哈哈哈哈。***
//做题的时候用vector存的图,写博客时想着用邻接表写一下,结果改了半个多小时,我太菜了。
//手写邻接表,和vector实现邻接表不太一样,使用vector,会额外增加一些时间,
//用vector的版本,跑了32ms;手写邻接表,跑了16ms;
#include<queue>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int num_e=100000+10;
const int num_p=1010;
const int inf=0x3f3f3f3f;
struct node 
{
	int v,w,next;
	node(){};
	node(int a,int b)
	{
		v=a;w=b;
	}
	
	bool operator < (const node & s) const
	{
		return w > s.w;
	}
}edge[num_e];
int head[num_p];
int from[num_e],to[num_e],worth[num_e];
int date[2][num_e];
int n,m,x,cnt;
void int_i(void)
{
	cnt=0;
	for(int j=1;j<=n;j++)
	head[j]=0;
	return ;
}
void addedge(int a,int b,int c)
{
	edge[++cnt].v=b;
	edge[cnt].w=c;
	edge[cnt].next=head[a];
	head[a]=cnt;
	return ;
}
void dijkstra(int g)
{
	int dis[n+1];
	int book[n+1];
	for(int i=1;i<=n;i++)
	{
		dis[i]=inf;
		book[i]=0;
	}
	dis[x]=0;
	
	priority_queue<node>q;//一定要用优先队列啊,就是因为这里调了半个小时,感谢pxgg。
	q.push(node(x,0));
	
	while(!q.empty())
	{
		node x=q.top();q.pop();
		
		if(book[x.v])
		continue;
		book[x.v]=1;
		
		for(int i=head[x.v];i;i=edge[i].next)
		{
			int y=edge[i].v;
			if(book[y]) continue;
			
			if(dis[y] > x.w + edge[i].w)
			{
				dis[y]=x.w + edge[i].w;
				q.push(node(y,dis[y]));
			}
		}
	}
	
	for(int i=1;i<=n;i++)
	{
		date[g][i]=dis[i];
	}
	return ;
}
int main()
{
	scanf("%d%d%d",&n,&m,&x);
	int_i();
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&from[i],&to[i],&worth[i]);
		addedge(from[i],to[i],worth[i]);
	}
	
	dijkstra(0);
	
	int_i();
	//将边倒着存储,再跑一次
	for(int i=1;i<=m;i++)
	{
		addedge(to[i],from[i],worth[i]);
	}
	dijkstra(1);
	
	int maxm=0;
	for(int i=1;i<=n;i++)
	maxm=max(maxm,date[0][i]+date[1][i]);
	printf("%d\n",maxm);
	return 0;
} 

//vector版本,跑了32ms
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long LL;
const int inf=0x3f3f3f3f;
const int num=1010;
struct edge
{
	int u,v,w;
	edge(int a,int b,int c)
	{
		u=a;v=b;w=c;	
	}	
};
vector<edge>e[num];
int from[num],to[num],time[num];//´æ´¢Êý¾Ý 
int date[2][num];
struct point
{
	int id,n_dis;
	point(int a,int b)
	{
		id=a;n_dis=b;	
	}	
	bool operator < (const point &a) const
	{
		return n_dis > a.n_dis; 
	}
};
void dijkstra(int g,int x,int n)
{
	int dis[num];
	bool book[num];
	
	for(int i=1;i<=n;i++) 
	{
		dis[i]=inf;
		book[i]=false;
	}
	dis[x]=0;
	
	priority_queue<point>q;
	q.push(point(x,0));
	
	while(!q.empty())
	{
		point x=q.top();
		q.pop();
		
		if(book[x.id])
		continue;
		book[x.id]=true;
		
		for(int i=0;i<e[x.id].size();i++)
		{
			edge y=e[x.id][i];
			
			if(book[y.v])
			continue;
			
			if(dis[y.v] > x.n_dis + y.w)
			{
				dis[y.v] = x.n_dis + y.w;
				q.push(point(y.v,dis[y.v]));
			}
		}
	}
	
	for(int i=1;i<=n;i++)
	date[g][i]=dis[i];
	
	return ;
}
int main()
{
	int x,n,m;
	int u,v,w;
	int sum=0;

	scanf("%d%d%d",&n,&m,&x);
		
	for(int i=1;i<=n;i++)
	{
		e[i].clear();
	}
		
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d%d",&from[i],&to[i],&time[i]);
		e[to[i]].push_back(edge(to[i],from[i],time[i]));
	}
	dijkstra(0,x,n);
	
	for(int i=1;i<=n;i++)
	{
		e[i].clear();
	}
	
	for(int i=1;i<=m;i++)
	{
		e[from[i]].push_back(edge(from[i],to[i],time[i]));
	}
	
	dijkstra(1,x,n);
		
	sum=0;
	for(int i=1;i<=n;i++)
	sum=max(date[0][i]+date[1][i],sum);
	
	printf("%d\n",sum);

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值