Hduoj2433【SPFA】

/*Travel
Time Limit: 10000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2364    Accepted Submission(s): 776


Problem Description
One day, Tom traveled to a country named BGM. BGM is a small country, but there are N (N <= 100) towns in it. Each town products one kind of food, the food will be transported to all the towns. In addition, the trucks will always take the shortest way. There are M (M <= 3000) two-way roads connecting the towns, and the length of the road is 1.
Let SUM be the total distance of the shortest paths between all pairs of the towns. Please write a program to calculate the new SUM after one of the M roads is destroyed.


Input
The input contains several test cases.
The first line contains two positive integers N, M. The following M lines each contains two integers u, v, meaning there is a two-way road between town u and v. The roads are numbered from 1 to M according to the order of the input.
The input will be terminated by EOF.


 

Output
Output M lines, the i-th line is the new SUM after the i-th road is destroyed. If the towns are not connected after the i-th road is destroyed, please output “INF” in the i-th line. 

 

Sample Input
5 4
5 1
1 3
3 2
5 4
2 2
1 2
1 2
 

Sample Output
INF
INF
INF
INF
2
2
 

Source
2008 Asia Chengdu Regional Contest Online 
*/
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<math.h>
#define INF 99999999
int n, m, map[101][101], sum[101];//map邻接表,sum用来保存初始的小镇i为起点的最短路和
int u[3010], v[3010], fa[101], edge[101][3001];//fa保存最短路的每个点的前驱
bool mark[101][101][101];
//mark用来保存每个小镇的最短路需要经过的边
//求s为起点的最短路
int spfa(int s)
{
	int i, j, k, q[3010], front = 0, rear = 0, d[101], vis[101];
	//initial
	memset(vis, 0, sizeof(vis));
	for(i = 1; i <= n; ++i)
	{
		d[i] = INF;
		fa[i] = i;
	}
	d[s] = 0;
	q[rear++] = s;//push
	while(front < rear)
	{
		k = q[front++];
		vis[k] = 0;//pop
		for(i = 1; i <= edge[k][0]; ++i)
		{
			int t = edge[k][i];   //连接点的编号
			if(map[k][t] > 0 && d[t] > d[k] + 1)
			{
				d[t] = d[k]+1;
				fa[t] = k;    
				if(!vis[t])   //未入队
				{
					q[rear++] = t;
					vis[t] = 1;//push
				}
			}
		}
	}
	j = 0;
	for(i = 1; i <= n; ++i)
	j  += d[i];
	return j;   返回以小镇i为起点的路径和
}
//标记最短路径经过的边
void get_mark(int s)
{
	for(int i = 1; i <= n; ++i)
	if(fa[i] != i)
	{
		mark[s][fa[i]][i] = true;
		mark[s][i][fa[i]] = true;
	}
}
int main()
{
	int i, j, k;
	while(scanf("%d%d", &n, &m) != EOF)
	{
		memset(map, 0, sizeof(map));
		memset(mark, 0, sizeof(mark));
		memset(edge, 0, sizeof(edge));
		//count and save edges
		for(i = 0; i < m; ++i)
		{
			scanf("%d%d", &u[i], &v[i]);
			edge[u[i]][0]++;
			edge[v[i]][0]++;
			edge[u[i]][edge[u[i]][0]] = v[i];
			edge[v[i]][edge[v[i]][0]] = u[i];
			map[u[i]][v[i]]++;   //标记重复边
			map[v[i]][u[i]] = map[u[i]][v[i]];
		}
		int ans = 0;
		//保存最初的小镇的路径和
		for(i = 1; i <= n; ++i)
		{
			sum[i] = spfa(i);
			get_mark(i);   
			ans += sum[i];
		}
		
		for(i = 0; i < m; ++i)
		{	
			if(map[u[i]][v[i]] > 1)//如果重复边则删除后SUM不变
			{
				printf("%d\n", ans);
				continue;
			}
			int temp = ans;
			for(j = 1; j <= n; ++j)
			{
				//如果最短路经过此边
				if( mark[j][u[i]][v[i]] )
				{
					map[u[i]][v[i]] = map[v[i]][u[i]] = 0;//destroyed
					temp += spfa(j);
					if(temp >= INF)
					{
						map[u[i]][v[i]] = map[v[i]][u[i]] = 1;
						break;	
					}
					temp -= sum[j];
					map[u[i]][v[i]] = map[v[i]][u[i]] = 1;
				}
			}
			if(temp < INF)
			printf("%d\n", temp);
			else
			printf("INF\n");
		}
	}
	return 0;
}


题意:总共有n个小镇,每个小镇i都要运送一种特产到其他的小镇j,并且运送路线总是最短路,小镇i的路线总和即为其他所有小镇j到i的最短路的总和,题目中的SUM则为所有i的和,现在给出m条小路,求第i条小路被破坏后的SUM。对每一个i输出一个SUM。

思路:这里最原始的方法就是暴力,对每一条小路被破坏后的图进行SPFA,然后计算总和,但是这必定是超时的。

这里的想法是对于这个总和,我们需要对每一个小镇进行SPFA,求出其他小镇到该小镇的最短路,他们的和即为该小镇的运送路线的长度,最后对每一个小镇的运送路线进行求和即为最初的SUM。然而题目要求的是求出每一条i被破坏后的SUM,如果每次重新构图并求最短路和求和是很费时间的。

那么我们可以 这么考虑,我们将每个i的路线总和求出并保留他们最短路径所经过的边,然后对于每一条被破坏的边,我们对每个i小镇进行查询,看其中最短路径是否经过这条边,倘若经过,则当前i小镇的最短路总和需要重新计算,即进行一SPFA。若不包含,则其和为原始数据。对每一个小镇重新遍历后,再计算SUM。

这里有几个难点,一个是里面有重复边,对于重复边我们构图时可以用map记录重复边的条数,同时记录边。 

其次就是保存的小镇的相连点,需要构图和计数和保存编号,最大程度的简化时间复杂度。

还有一个要注意的是由于边是双向的,所以赋值时需要双向赋值,莫忘莫忘。。。

你可能感兴趣的:(Hduoj2433【SPFA】)