最短路。这题看上去像在找路由器。。
这个图的边通过给定每个点的邻接点的方式给出,每条边都默认权值是1
,且是无向的。然后再给出一些公交路线,把这些公交路线包含的所有点组成一个集合(是全图的点集的子集),要求在全图中找一个点,使得这个点到这个集合的距离最小(若相等则输出id
最小的点)。点到集合的距离
定义为这个点到这个集合中所有点的最短路的最大值。
最小找最大再找最小,我们可以先看前两个步骤。我们现在需要求每个点到这个集合s
的距离,也就是需要求出每个点到|s|
个点的最短路,显而易见的方法是每个点都来一遍spfa
。但是,稍微想一想就会发现,nz
的上限是9999
,显然吃不消。再转念一想,是不是可以从s
中每个点出发?再一看题,发现nr*mr
的上限只有200
。没错。为什么能这样转化?因为如果是前者的话,求了大量无用的最短路(那些终点为非s
集点的最短路求了都没用);而后者求的每条最短路都可以看成是任意点到s
集点的最短路的反向路径。(而且因为是无向图,正向和反向是一回事,无需再反向建边)
其实类比HDU 1535,我们可以把这个s
集看成一个点(想想它的体量和全图点集相比),就知道怎么做了。
还有几个点:
- 给的点的
id
并不以输入的N
为取值范围,N
只代表点的个数,id
的取值范围由题目中的上限给出。 - 需要在
spfa
中初始d[s]=1
(因为题目这样定义,这么做的可行性:想象有一个虚拟起点只连接起点)(不初始为1
的话,最后输出++ans
也行)。 - 其实在相同情况下输出最小的
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;
}