题意:给定一个无向图,其中K个点有监控,每个监控在第一次访问该点的时候做记录,现在给出其中L个监控点的访问顺序,问是否存在路线使得满足上述顺序同时访问所有点。可以重复访问同一个点,但是有监控的只会在第一次访问做记录。
昨天比赛的时候挂了几发才过,代码也写得不成样子了。
今天重新想了下,发现其实只要写搜索就行,BFS或DFS都行。
昨天用的是BFS+并查集,并查集主要是判断图是否联通。
首先因为有K个监控,如果遍历完所有点肯定有L个记录,如果 L小于K就直接输出"No"。
接下来,先将读到的L个点标记为不可扩展ban[i]=true。
这里不妨设是从第一个记录到的点出发,先标记它为vis[i]=true
接下来按顺序访问L个记录,如果当前记录vis[x]=false,说明不能访问到它,return false
否则对它进行bfs,bfs到的点全部标记为vis[]=true,如果这个点还是ban[]=true的状态,就不把它加入队列。同时用一个extend[]表示是否扩展过,扩展过的就不要扩展了。
最后如果全部L个点访问完了,再扫一遍vis,看看是否还有未访问到的,这样就可以不用再写个并查集了。
PS:据说也有只用并查集的做法,待学习。
#include<cstdio>
#include<cstring>
#include<vector>
#include<queue>
using namespace std;
const int N = 100010;
#define pb push_back
vector<int> V[N];
bool vis[N], extend[N], ban[N];
int T, m, n, K, L, l[N];
void bfs(int x){
queue<int> Q;
Q.push(x);
while(!Q.empty()){
x = Q.front(); Q.pop();
if(extend[x]) continue;
extend[x] = 1;
for(int i=0; i<V[x].size(); i++){
int j = V[x][i];
if(vis[j]) continue;
vis[j] = 1;
if(!ban[j]) Q.push(j);
}
}
}
bool solve(){
vis[l[0]] = 1;
for(int i=0; i<L; i++){
if(!vis[l[i]]) return 0;
ban[l[i]] = 0;
bfs(l[i]);
}
for(int i=1; i<=n; i++) if(!vis[i]) return 0;
return 1;
}
int main(){
scanf("%d", &T);
while(T--){
scanf("%d %d %d", &n, &m, &K);
for(int i=1; i<=n; i++){
V[i].clear();
vis[i] = 0;
ban[i] = 0;
extend[i] = 0;
}
int a,b;
for(int i=1; i<=K; i++) scanf("%d", &a);
while(m--){
scanf("%d %d", &a, &b);
V[a].pb(b);
V[b].pb(a);
}
scanf("%d", &L);
for(int i=0; i<L; i++){
scanf("%d", l+i);
if(i) ban[l[i]] = 1;
}
if(L<K) puts("No");
else puts(solve()?"Yes":"No");
}
return 0;
}