[转]HDU 2433 Travel 最短路 预处理优化

    原文地址:http://http://blog.csdn.net/linraise/article/details/42155947

    题意是说算出一个图所有点到其他点的最短路的和,然后进行删除某条边再进行处理,对SPFA本来也不是很熟,改来改去始终没能A掉,所以转载一篇,参考代码= =

   

思路:

个人认为这道题的描述有问题,但是不在意这些细节了。题目的大致意思是:给定一个图,邻接顶点距离恒为1,

对每一个点都算出其它任意的点到它的最小距离总和。再把所有点的总和相加得到的就是结果。顶点数为N,因为

邻接点距离恒为1,可以用BFS生成一棵以当前顶点为根的最小树。所有节点到根的距离总和就是一个顶点的距离。

如果使用蛮力法,生成一棵最小树BFS时间复杂度为O(n),n<100,共有n个结点O(n*n),每删一条边(m)就需要重新生成,

总复杂度为O(n*n*m)。显然是不可接受的。题目的用意也清晰,就是要我们空间换时间,优化之。没有必要每删一条边就

重新生成一棵BFS最小树。如果删的边不是BFS最小树的树边,忽略之。直接记录最小树的距离结果。

优化点一容易得出:每生成一棵BFS树,就把它的距离记录下来。如果删边不在树上,则直接取之。

如果图本身就不是连通图,则可以输出m个INF就完事,优化点二得到。

优化点三也容易想到:如果删除的边是重边,则也是无影响的。

最坏的情况:删的边刚好是树边,则老实地重新BFS生成最小树。(但是需要十分注意不能破坏图结构和污染已记录的BFS树距离)

//2433 bround first search
//题意真是不清楚,早说是所有点距离全加起来啊

#include 
#include 
#include 
using namespace std;

typedef struct Edge
{
	int beg,end;
} Edge;

Edge edge[3001];
int Sum[101] = {0};
int Map[101][101];
bool visited[101];
int prec[101][101];		// prefix,prec[i] store the i-th bfs tree
int N,M;

int BFS(int k,bool is_preprocess = true)
{
	int dis[101] = {0};
	memset(visited,false,sizeof(visited));
	queue Q;
	visited[k] = true;
	Q.push(k);
	int cnt = 1;
	int Sum = 0;
	while (!Q.empty())
	{
		int i = Q.front();Q.pop();
		for (int j=1; j <= N; ++j)
		{
			if (!visited[j] && Map[i][j] > 0)
			{
				visited[j] = true;
				if (is_preprocess)
					prec[k][j] = i;	//记录前驱,只记录第一次遍历的BFS树的节点前驱
				dis[j] = dis[i] + 1;
				Sum += dis[j];
				++cnt;
				Q.push(j);
			}
		}
	}
	return (cnt==N) ? Sum : -1;
}

int main()
{
#ifndef ONLINE_JUDGE
	freopen("1.txt","r",stdin);
#endif

	int x,y, sum ;
	while (cin >> N >> M)
	{
		memset(Sum,0,sizeof(Sum));
		memset(Map,0,sizeof(Map));
		memset(prec,0,sizeof(prec));
		int i=1;
		for ( ;i <= M; ++i)
		{
			//build the graph
			cin >> x >> y;
			Map[x][y]++;
			Map[y][x]++;
			edge[i].beg = x;
			edge[i].end = y;
		}
		sum = 0;
		i=1;
		for (; i <= N; ++i)
		{
			Sum[i] = BFS(i);
			if (Sum[i]==-1)
				break;
			else
				sum += Sum[i];
		}
		if (i <= N)		//原图为非连通图,无论删哪条边,距离恒为INF
		{
			for (int i=0; i < M; ++i)
				puts("INF");
			continue;
		}
		for (int i=1; i <= M; ++i)
		{
			int x = edge[i].beg;
			int y = edge[i].end;
			if (Map[x][y] > 1)			//有重边,删除其中一条对全图无影响
			{
				cout << sum << endl;
			}
			else					//删的是割边(不可能有Map[][]==0,因为前边过滤了非连通图)
			{
				int sum1 = 0,j=1,s1;
				for (; j <= N; ++j)//遍历全部顶点,蛮力法
				{
					if (prec[j][y]==x || prec[j][x]==y)//x-y在第j棵bfs树上,只能蛮力
					{
						Map[x][y] = Map[y][x] = 0;	//删边

						s1 = BFS(j,false);
						if (s1==-1)	//非连通,直接返回INF
						{
							Map[x][y] = Map[y][x] = 1;	//恢复边
							break;
						}
						sum1 += s1;
						Map[x][y] = Map[y][x] = 1;	//恢复边
					}
					else
					{
						sum1 += Sum[j];
					}
				}
				if (j <= N)
				{
					puts("INF");
					//	continue;
				}
				else
				{
					cout << sum1 << endl;
				}
			}
		}
	}
	return 0;
}


你可能感兴趣的:(HDU)