题目链接:传送门
题意描述:
给你一颗n个结点的无向树,然后给你m条路径(a, b),让你求最小的点集,满足这些路径上至少有一点在这个点集上。输出点集的大小和点。
题目分析:
这道题的解法是贪心,先创建n个集合,这些集合就是点放路径的集合,在输入路径的时候,如果a==b,那么a这个点肯定是要的。否则,就在a、b的集合里面放序列号,然后就行暴力dfs贪心。如果一个点中集合的序列号和它相邻的点的序列号如果出现其中一个序列号相同的话,那么,这一点肯定要放进答案里面,并把这个点的集合删除掉,如果不同,就把相邻点的集合转移到这个点来。为什么可以这样贪心呢?这有LCA的原理,当两个相同的序列碰到的时候,那么肯定能说明这个这个点的集合已经是最大的了,如果在转移的话会出现漏掉路径的情况了。所以可以这样子贪心。
代码:
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1e5+5;
int n, m;
set<int>Q[maxn];
vector<int>M[maxn];
vector<int>res;
int ans[maxn], tot = 0;
void dfs(int u, int fa){
for(int i=0;i<M[u].size();++i){
int v = M[u][i];
if(v != fa){
dfs(v, u);
if(Q[u].size() < Q[v].size()) swap(Q[u], Q[v]);
for(auto t : Q[v]){
if(Q[u].find(t) != Q[u].end()){
ans[u] = 1;
break;
}else{
Q[u].insert(t);
}
}
}
}
if(ans[u] == 1){
res.push_back(u);
tot++; Q[u].clear();
}
}
int main(){
cin >> n;
int u, v;
for(int i=1;i<n;++i){
scanf("%d %d", &u, &v);
M[u].push_back(v);
M[v].push_back(u);
}
cin >> m;
for(int i=0;i<m;++i){
scanf("%d %d", &u, &v);
if(u == v){
ans[u] = 1; continue;
}
Q[u].insert(i);
Q[v].insert(i);
}
dfs(1, 0);
cout << res.size() << endl;
for(int i=0;i<res.size()-1;++i){
printf("%d ", res[i]);
}
cout << res[res.size()-1] << endl;
return 0;
}