Codeforces Gym101170I:Iron and Coal(建多幅图+多次BFS)***

题目链接

题意

有n个点,其中有m个点是铁矿,k个点是煤,从1号点出发,你可以派一些士兵跑向不同的点,问占领至少一个铁矿和一个煤的时候,最少需要占领多少个点。

思路

建两幅图,其中一幅是正向边,一幅是反向边。做三次BFS。
第一遍BFS:从1号点BFS一遍整个正向边的图,记录数组dis[0][i]为每个点距离1号点的距离。O(n)
第二遍BFS:从每个铁矿BFS一遍整个反向边的图,记录数组dis[1][i]为每个点距离每个铁矿的最近距离。O(n)
第三遍BFS:从每个煤BFS一遍整个反向边的图,和第二遍类似。
对于每个点,三个dis数组求和就是从一号点到最近的铁矿和最近的煤的距离和。

#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
const int INF = 0x3f3f3f3f;
vector<int> g[2][N];
int o[N], c[N], b[2] = {1, 1}, dis[3][N];
void bfs(int e, int n, int p[], int id) {
	queue<int> que; while(!que.empty()) que.pop();
	memset(dis[id], INF, sizeof(dis[id]));
	for(int i = 1; i <= n; i++) que.push(p[i]), dis[id][p[i]] = 0;
	while(!que.empty()) {
		int u = que.front(); que.pop();
		for(int i = 0; i < g[e][u].size(); i++) {
			int v = g[e][u][i];
			if(dis[id][v] != INF) continue;
			dis[id][v] = dis[id][u] + 1;
			que.push(v);
		}
	}
}
int main() {
	int n, m, k; scanf("%d%d%d", &n, &m, &k);
	for(int i = 1; i <= m; i++)	scanf("%d", &o[i]);
	for(int i = 1; i <= k; i++) scanf("%d", &c[i]);
	for(int i = 0; i < 2; i++) for(int j = 1; j <= n; j++) g[i][j].clear();
	for(int i = 1; i <= n; i++) {
		int a; scanf("%d", &a);
		for(int j = 1; j <= a; j++) {
			int v; scanf("%d", &v);
			g[0][i].push_back(v);
			g[1][v].push_back(i);
		}
	}
	bfs(0, 1, b, 0);
	bfs(1, m, o, 1);
	bfs(1, k, c, 2);
	int ans = INF;
	for(int i = 1; i <= n; i++) {
	//	printf("%d : %d - %d - %d
", i, dis[0][i], dis[1][i], dis[2][i]);
		if(dis[0][i] == INF || dis[1][i] == INF || dis[2][i] == INF) continue;
		ans = min(ans, dis[0][i] + dis[1][i] + dis[2][i]);
	}
	if(ans == INF) puts("impossible");
	else printf("%d
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/fightfordream/p/7215320.html