并查集 + Bfs 之 zoj 3811 Untrusted Patrol

//  [9/12/2014 Sjm]
/*
此题在大神的帮助下AC了。。。
要求:output "Yes" if the security man worked normally and has checked all piles of drinks, or "No" if not.
(1)check all piles of drinks: 
	1) L == K (L 是 gathered from all sensors,所以若L < K,则必然是有些带有传感器的节点没有走,无法满足条件)
	2) 图一定是连通,否则从一个点出发无法走遍所有节点 (通过并查集判断)
(2)worked normally:
	意思巡逻的路线是可以根据图中所给的边找出来的,
	而不是digs through walls, climb over piles, use some black magic to teleport to anywhere and so on.
	故而需要判断能否找到一个正常的巡逻路线。。。
	( 注意正常的巡逻路线是可以重复走途中所给的边,
	   即若A与B相连,A与C相连,则如果从A到B后,亦可从B返回A,再到C)
	思路:
	1)从第 K 个传感器所记录的点出发,寻找到其所有能到达的传感器所记录的点。。转向 2)
	2)判断第 K+1 个传感器所记录的点,是否可由前K个传感器所记录的点所到达
		若否,则即可判断正常的巡逻路线不存在。。。
		若是,则 K = K + 1,不断进行操作 2),直至所有传感器的点均可被到达,可判断正常的巡逻路线存在。。。
	关键:
	如何用计算机去求 “从第 K 个传感器所记录的点出发,寻找到其所有能到达的传感器所记录的点”?
	解法就像是自动机:
		假设从点 A (此点即队列中第一个点)出发,采用 Bfs 找出该点能到达的所有点,
		在这些点中,对于传感器记录的点进行标记,而对于非传感器记录的点,放到队列中,
		再将队列中的点取出,继续寻找其能到达的所有点,同点 A 进行的操作处理。。
		直到队列为空,则说明此时对于点 A 能到达的传感器记录的点都已标记下来。。。
*/
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <vector>
#include <queue>
using namespace std;
const int MAX_N = 100005;
int N, M, K;
vector<int> Edge[MAX_N];
bool isVis[MAX_N];
bool isIn[MAX_N];
int verOrder[MAX_N];
int father[MAX_N];

void Del() {
	for (int i = 1; i <= N; ++i) {
		if (!Edge[i].empty()) {
			Edge[i].clear();
		}
	}
}

int myFind(int x) {
	if (x == father[x]) return x;
	else return father[x] = myFind(father[x]);
}

bool judge_Connected_Graph() {
	int ver = myFind(N);
	for (int i = 1; i < N; ++i) {
		if (myFind(i) != ver) {
			return false;
		}
	}
	return true;
}

bool Bfs() {
	isVis[verOrder[0]] = true;
	queue<int> que;
	for (int i = 0; i < K; ++i) {
		if (isVis[verOrder[i]]) que.push(verOrder[i]);
		else return false;
		while (!que.empty()) {
			int ver = que.front();
			que.pop();
			for (int i = 0; i < Edge[ver].size(); ++i) {
				int t_ver = Edge[ver][i];
				if (!isVis[t_ver]) {
					if (isIn[t_ver]) isVis[t_ver] = true;
					else {
						isVis[t_ver] = true;
						que.push(t_ver);
					}
				}
			}
		}
	}
	return true;
}

int main() {
	//freopen("input.txt", "r", stdin);
	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%d %d %d", &N, &M, &K);
		for (int i = 1; i <= N; ++i) {
			isIn[i] = false;
			isVis[i] = false;
			father[i] = i;
		}
		int sensor;
		for (int i = 0; i < K; ++i) {
			scanf("%d", &sensor);
			isIn[sensor] = true;
		}
		int u, v;
		for (int i = 0; i < M; ++i) {
			scanf("%d %d", &u, &v);
			Edge[u].push_back(v);
			Edge[v].push_back(u);
			int tmp1 = myFind(v);
			int tmp2 = myFind(u);
			if (tmp1 != tmp2) {
				father[tmp1] = tmp2;
			}
		}
		int sum;
		scanf("%d", &sum);
		for (int i = 0; i < sum; ++i) {
			scanf("%d", &sensor);
			verOrder[i] = sensor;
		}
		if ((sum < K) || (!judge_Connected_Graph()) || (!Bfs())) {
			printf("No\n");
		}
		else printf("Yes\n");
		Del();
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值