题意是 n 个点,m条有向边。其中有些点有资源a,有些点有资源b,从点1出发,最小花费多少代价能同时占据最少一个含 a 的和含 b 的。花费的代价为路径上的点数,注意不一定只能一条路径。
首先想路径主要分三类,1是去a和去b是两条不相交的路径,2是从点1出发到达某个分岔路分别到a和b,3是a和b的资源在一条路径上。于是原图需要做三次bfs,分别是点1到每个点的最短距离,后两次是有资源a的和属性b的通过反向边到达每个点的最短距离。于是以任意节点作为分叉口的总代价就是这三个距离加起来,而且通过枚举分叉口能把上述的三种路径情况都包括进去。
#include <bits/stdc++.h>
using namespace std;
const int maxn = 200010, maxx = 0x1f3f3f3f;
vector<int> G[maxn], F[maxn], T[3];
int dis[maxn][3], n, m, k, x, y, st, ans;
struct Node {
int x;
Node() {}
Node(int xx): x(xx) {}
bool operator<(const Node other) const {
return dis[x][st] > dis[other.x][st];
}
};
priority_queue<Node> q;
void dij() {
if (st == 0) {
while (!q.empty()) {
int u = q.top().x;
q.pop();
for (int i = 0; i < G[u].size(); i++) {
int v = G[u][i];
if (dis[v][st] != maxx) continue;
dis[v][st] = dis[u][st]+1;
q.push(v);
}
}
} else {
while (!q.empty()) {
int u = q.top().x;
q.pop();
for (int i = 0; i < F[u].size(); i++) {
int v = F[u][i];
if (dis[v][st] != maxx) continue;
dis[v][st] = dis[u][st]+1;
q.push(v);
}
}
}
}
int main() {
scanf("%d %d %d", &n, &m, &k);
for (int i = 1; i <= n; i++)
for (int j = 0; j < 3; j++) dis[i][j] = maxx;
ans = maxx;
dis[1][0] = 0;
for (int i = 1; i <= m; i++) { scanf("%d", &x); T[1].push_back(x); dis[x][1] = 0;}
for (int i = 1; i <= k; i++) { scanf("%d", &x); T[2].push_back(x); dis[x][2] = 0;}
for (int i = 1; i <= n; i++) {
scanf("%d", &x);
while (x--) {
scanf("%d", &y);
G[i].push_back(y);
F[y].push_back(i);
}
}
while (!q.empty()) q.pop();
st = 0;
q.push(Node(1));
dij();
while (!q.empty()) q.pop();
st = 1;
for (int i = 0; i < T[1].size(); i++) q.push(Node(T[1][i]));
dij();
while (!q.empty()) q.pop();
st = 2;
for (int i = 0; i < T[2].size(); i++) q.push(Node(T[2][i]));
dij();
for (int i = 1; i <= n; i++) {
ans = min(ans, dis[i][0]+dis[i][1]+dis[i][2]);
}
if (ans == maxx) printf("impossible\n");
else printf("%d\n", ans);
}