题目链接:点击查看
题目大意:给出一张 n 个点和 m 条边组成的有向无环图,规定:每个点的出度一定小于等于 2 ,现在要求删除掉至多 个点,使得任意一条路径的长度都不大于 1
题目分析:主要是这个出度小于等于 2 和 4/7 有点难搞,在看过题解后,可以这样考虑,因为每个点的出度都小于等于 2 ,那么我们不妨令其出度都为 2 ,可以得到一颗完全二叉树:
这样的话,一个高度为三的完全二叉树,恰好有 7 个结点,如果我们将第三层的 4 , 5 , 6 , 7 这四个结点都删除掉的话,剩下的结点显然是满足题意的,这样我们也就很巧妙的用到了题目给出的两个条件
换句话说,可以枚举每个顶点,将与该顶点可能构成长度为 2 路径的顶点都删掉即可,但是这样处理的话会有后效性,例如:
这样的一种图,我们会发现,在枚举顶点 1 的时候,就已经将点 3 , 4 , 5 都删掉了,但是这个例子我们只需要删掉 3 和 4 即可
注意一下题意给的提示,因为题意给的每条边 x -> y 都满足了 x < y ,所以 1 ~ n 的序列就已经满足拓扑排序了,对于任意一个节点 i 来说,前面的 i - 1 个结点肯定是不会再有影响了,所以我们可以倒着向前找,这个倒着的意思是将整个图置反,以节点 i 为起点向前去找,如果找到长度为 2 的路径的顶点时,将顶点 i 删掉即可,这样的查找就不具有后效性了
代码:
#include<iostream>
#include<cstdio>
#include<string>
#include<ctime>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<stack>
#include<climits>
#include<queue>
#include<map>
#include<set>
#include<sstream>
#include<cassert>
using namespace std;
typedef long long LL;
typedef unsigned long long ull;
const int inf=0x3f3f3f3f;
const int N=2e5+100;
vector<int>node[N];
bool vis[N];
int main()
{
#ifndef ONLINE_JUDGE
// freopen("input.txt","r",stdin);
// freopen("output.txt","w",stdout);
#endif
// ios::sync_with_stdio(false);
int w;
cin>>w;
while(w--)
{
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
node[i].clear();
vis[i]=false;
}
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
node[v].push_back(u);
}
vector<int>ans;
for(int i=1;i<=n;i++)
{
for(auto u:node[i])
if(!vis[u])
for(auto v:node[u])
if(!vis[v])
{
vis[i]=true;
ans.push_back(i);
goto end;
}
end:;
}
printf("%d\n",ans.size());
for(auto it:ans)
printf("%d ",it);
puts("");
}
return 0;
}