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記錄重複邊的條數,同時記錄邊。 

其次就是保存的小鎮的相連點,需要構圖和計數和保存編號,最大程度的簡化時間複雜度。

還有一個要注意的是由於邊是雙向的,所以賦值時需要雙向賦值,莫忘莫忘。。。

發佈了250 篇原創文章 · 獲贊 3 · 訪問量 8萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章