POJ1511_Invitation_Cards_SPFA算法求最短路

这道题写了我整整一天- -......

/*

吐槽:

也怪我自己效率低。刚开始看这道题说要求来回的最短路径和,觉得用Dijkstra(其实第一反应是最小生成树,但是发现来回的权值不一样- -),但是看数据规模就吓到了,100万个点,虽然有8000MS,但是觉得STL应该过不了,就放弃用Vector建树了,然后邻接表自己之前虽然已经敲过两遍代码了,但是还是很生。无奈上网搜解题报告,发现许多人用SPFA算法(表示今天第一次听这个算法),于是非常郁闷的去百度百科查了一下什么是SPFA算法,发现虽然名字很生,但是用法却很简单,也很容易理解,就是跟Dijkstra一样路径松弛就行了,用的也是非常亲切的队列Queue,于是去想这个题怎么做。网上搜了半天发现也有其他人用Dijkstra优化用优先队列做的,也有用Vector优化A掉的- -不过没有太详尽的注释,加之今天烦,就这么迷迷糊糊一直拖到了晚上才开始正式写。

*/

/*—————————————人家才不是分割线呢—————————————————*/

SPFA算法://转载于百度百科SPFA词条

很多时候,给定的图存在负权边,这时类似Dijkstra等算法便没有了用武之地,而Bellman-Ford算法的复杂度又过高,SPFA算法便派上用场了。
简洁起见,我们约定有向加权图G不存在负权回路,即最短路径一定存在。当然,我们可以在执行该算法前做一次拓扑排序,以判断是否存在负权回路,但这不是我们讨论的重点。
我们用数组d记录每个结点的最短路径估计值,而且用邻接表来存储图G。我们采取的方法是松弛:设立一个先进先出的队列用来保存待优化的结点,优化时每次取出队首结点u,并且用u点当前的最短路径估计值对离开u点所指向的结点v进行松弛操作,如果v点的最短路径估计值有所调整,且v点不在当前的队列中,就将v点放入队尾。这样不断从队列中取出结点来进行松弛操作,直至队列空为止。


题意:


有向图中求  原点到所有点的最短路之和  加上  所有点到原点的最短路之和  的总和 的最小值(好绕口)


解题思路:

第一反应是单源最短路求和很简单,反过来的话想把其余所有点都枚举一遍Dijkstra到原点(- -)一看数据规模还是放弃了。想半天想不到有效的解法,网上搜到大家都是用反向图做的,把所有的有向边反向再存起来,再求一次单源最短路径之和就行了(- -)反正我自己是没想到,太精p了(只能说我图论太渣)

这样一来就没有什么难点了,就正向反向做SPFA就行,用邻接表建图。

不过数据很坑,需要把sum开到long long


题目:Invitation Cards
Time Limit: 8000MS Memory Limit: 262144K
Total Submissions: 16415 Accepted: 5334

Description

In the age of television, not many people attend theater performances. Antique Comedians of Malidinesia are aware of this fact. They want to propagate theater and, most of all, Antique Comedies. They have printed invitation cards with all the necessary information and with the programme. A lot of students were hired to distribute these invitations among the people. Each student volunteer has assigned exactly one bus stop and he or she stays there the whole day and gives invitation to people travelling by bus. A special course was taken where students learned how to influence people and what is the difference between influencing and robbery.

The transport system is very special: all lines are unidirectional and connect exactly two stops. Buses leave the originating stop with passangers each half an hour. After reaching the destination stop they return empty to the originating stop, where they wait until the next full half an hour, e.g. X:00 or X:30, where 'X' denotes the hour. The fee for transport between two stops is given by special tables and is payable on the spot. The lines are planned in such a way, that each round trip (i.e. a journey starting and finishing at the same stop) passes through a Central Checkpoint Stop (CCS) where each passenger has to pass a thorough check including body scan.

All the ACM student members leave the CCS each morning. Each volunteer is to move to one predetermined stop to invite passengers. There are as many volunteers as stops. At the end of the day, all students travel back to CCS. You are to write a computer program that helps ACM to minimize the amount of money to pay every day for the transport of their employees.

Input

The input consists of N cases. The first line of the input contains only positive integer N. Then follow the cases. Each case begins with a line containing exactly two integers P and Q, 1 <= P,Q <= 1000000. P is the number of stops including CCS and Q the number of bus lines. Then there are Q lines, each describing one bus line. Each of the lines contains exactly three numbers - the originating stop, the destination stop and the price. The CCS is designated by number 1. Prices are positive integers the sum of which is smaller than 1000000000. You can also assume it is always possible to get from any stop to any other stop.

Output

For each case, print one line containing the minimum amount of money to be paid each day by ACM for the travel costs of its volunteers.

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

Run IDUserProblemResultMemoryTimeLanguageCode LengthSubmit Time
11760084chengtbf1511Accepted48164K1922MSC++2501B2013-07-10 20:24:34

代码:

#include<cstdio>
#include<cstring>
#include<queue>
#include<algorithm>
#define MAXN 1000000000
#define N 1000005
using namespace std;
typedef struct MyStruct
{
	int to,next;
	int val;
}EDGE;
EDGE edge[N],edge_re[N];//这里两个结构数组分别存两个图,正向图和反向图
int head[N],head_re[N];
int count_num,count_num_re;//以上两行都是为了建立邻接表的图而准备的
queue<int>Q;//建立队列进行SPFA
long long dis[N],dis_re[N];//存距原点的距离
long long sum;
bool flag[N];

void add_edge(int a,int b,int w)//这两段建图的邻接表代码还是用的模版- -
{
	edge[count_num].to=b;
	edge[count_num].next=head[a];
	edge[count_num].val=w;
	head[a]=count_num++;
}
void add_edge_re(int a,int b,int w)
{
	edge_re[count_num_re].to=b;
	edge_re[count_num_re].next=head_re[a];
	edge_re[count_num_re].val=w;
	head_re[a]=count_num_re++;
}
void spfa_find_distance()
{
	memset(flag,0,sizeof(flag));
	while (!Q.empty())//初始化
	{
		Q.pop();
	}
	Q.push(1);
	int i,son,temp;
	while (!Q.empty())
	{
		temp=Q.front();
		Q.pop();flag[temp]=0;
		for ( i = head[temp]; i !=-1; i=edge[i].next)
		{
			son=edge[i].to;
			if ( dis[son]>dis[temp]+edge[i].val)
			{
				dis[son]=dis[temp]+edge[i].val;//这里注意!!不管当前寻找的点是否在队列中,都要做松弛!				
				if(flag[son]==0) 				
				{
					flag[son]=1;
					Q.push(son);
				}
				
			}
		}
	}

}
void spfa_find_distance_re()
{
	memset(flag,0,sizeof(flag));
	while (!Q.empty())//初始化
	{
		Q.pop();
	}
	Q.push(1);
	int i,son,temp;
	while (!Q.empty())
	{
		temp=Q.front();
		Q.pop();flag[temp]=0;
		for ( i = head_re[temp]; i !=-1; i=edge_re[i].next)
		{
			son=edge_re[i].to;
			if ( dis_re[son]>dis_re[temp]+edge_re[i].val)
			{				
				dis_re[son]=dis_re[temp]+edge_re[i].val;	
				if(flag[son]==0)
				{
					flag[son]=1;
					Q.push(son);
				}
			}
		}
	}
}
int main()
{
	int t,p,q,i,a,b,w;
	scanf("%d",&t);
	while (t--)
	{
		scanf("%d%d",&p,&q);
		count_num=1;count_num_re=1;
		for ( i = 1; i <= p; i++)//初始化
		{
			dis[i]=MAXN;head[i]=-1;
			dis_re[i]=MAXN;head_re[i]=-1;
		}
		dis[1]=0;dis_re[1]=0;
		for ( i = 1; i <= q; i++)//个人觉得用这个比memset快,因为数组太大了,如果处理小数据的时候也把整个数组memset一遍不值吧
		{
			edge[i].next=0;edge_re[i].next=0;
			edge[i].to=0;edge_re[i].to=0;
			edge[i].val=0;edge_re[i].val=0;
		}
		sum=0;

		for ( i = 1; i <=q ; i++)
		{
			scanf("%d%d%d",&a,&b,&w);
			add_edge(a,b,w);add_edge_re(b,a,w);
		}
		spfa_find_distance();
		spfa_find_distance_re();
		for ( i = 1; i <=p; i++)
		{
			sum+=dis[i]+dis_re[i];
		}
		printf("%lld\n",sum);

	}
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值