HDU 2377 Bus Pass

传送门

最短路。这题看上去像在找路由器。。
这个图的边通过给定每个点的邻接点的方式给出,每条边都默认权值是1,且是无向的。然后再给出一些公交路线,把这些公交路线包含的所有点组成一个集合(是全图的点集的子集),要求在全图中找一个点,使得这个点到这个集合的距离最小(若相等则输出id最小的点)。点到集合的距离定义为这个点到这个集合中所有点的最短路的最大值。

最小找最大再找最小,我们可以先看前两个步骤。我们现在需要求每个点到这个集合s的距离,也就是需要求出每个点到|s|个点的最短路,显而易见的方法是每个点都来一遍spfa。但是,稍微想一想就会发现,nz的上限是9999,显然吃不消。再转念一想,是不是可以从s中每个点出发?再一看题,发现nr*mr的上限只有200。没错。为什么能这样转化?因为如果是前者的话,求了大量无用的最短路(那些终点为s集点的最短路求了都没用);而后者求的每条最短路都可以看成是任意点到s集点的最短路的反向路径。(而且因为是无向图,正向和反向是一回事,无需再反向建边)

其实类比HDU 1535,我们可以把这个s集看成一个点(想想它的体量和全图点集相比),就知道怎么做了。

还有几个点:

  1. 给的点的id并不以输入的N为取值范围,N只代表点的个数,id的取值范围由题目中的上限给出。
  2. 需要在spfa中初始d[s]=1(因为题目这样定义,这么做的可行性:想象有一个虚拟起点只连接起点)(不初始为1的话,最后输出++ans也行)。
  3. 其实在相同情况下输出最小的id只需要从小到大枚举id计算ans就行了。

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <vector>
#include <cstring>
#include <string>
#include <queue>
#include <set>
using namespace std;                   // 最小选最大再选最小

const int INF = 1e9;
const int MAXN = 10000;
int N, M, T;

vector<int> v[MAXN];
int d[MAXN];
bool vis[MAXN];

set<int> s;                            // 存储公交途径点的集合
int max_d[MAXN];                       // 存储每个点到s集的最短路的最大值

void init()
{
	for (int i = 1; i < MAXN; i++)     // 给了N值,但并不意味点的ID在1~N之间
		v[i].clear();
	memset(max_d, -1, sizeof max_d);
	s.clear();
}

void spfa(int s0)
{
	memset(vis, 0, sizeof vis);
	fill(d + 1, d + MAXN, INF);
	queue<int> q;

	vis[s0] = true;
	d[s0] = 1;                             ////
	q.push(s0);
	
	for (; !q.empty();)
	{
		int t = q.front();
		q.pop();
		vis[t] = false;
		for (int i = 0; i < v[t].size(); i++)
		{
			int n = v[t][i];
			if (d[t] + 1 < d[n])
			{
				d[n] = d[t] + 1;
				if (!vis[n])
				{
					q.push(n);
					vis[n] = true;
				}
			}
		}
	}
}

int main()
{
	int a, b, c;
	scanf("%d", &T);
	for (; T--;)
	{
		scanf("%d%d", &N, &M);
		init();
		for (int i = 0; i < N; i++)
		{
			scanf("%d%d", &a, &b);
			for (; b--;)
			{
				scanf("%d", &c);
				v[a].push_back(c);       // 虽然是无向图,但没有必要再加上反向边,因为之后那个点肯定还会输入
			}
		}
		for (; M--;)
		{
			scanf("%d", &a);
			for (; a--;)
			{
				scanf("%d", &b);
				s.insert(b);
			}
		}

		for (set<int>::iterator it = s.begin(); it != s.end(); it++)  // auto it
		{
			spfa(*it);
			for (int i = 1; i < MAXN; i++)
				max_d[i] = max(max_d[i], d[i]);
		}

		int ans = INF;
		int ans_id;
		//int min_id = INF;
		for (int i = 1; i < MAXN; i++)
		{
			if (max_d[i] < ans)
			{
				ans = max_d[i];
				ans_id = i;
				//min_id = i;
			}
			/*else if (max_d[i] == ans)
			{
				min_id = min(min_id, i);
			}*/
		}
		printf("%d %d\n", ans, ans_id);
	}

	return 0;
}
posted @ 2019-04-03 17:43  CrossingOver  阅读(204)  评论(0编辑  收藏  举报