简述题意:一张无自环的有向图,请找出一个点集Q,满足Q内的点互相没有边,且Q内的点一定可以通过1次或2次移动,到达Q集合以外的任意一个点。
time limit per test 2 seconds
memory limit per test256 megabytes
算法:玄学思路
难度:NOIP
题解:两次for循环找出答案!
- 第一层for循环:从小到大枚举点的编号,将与此点相连的点打上标记,这样就维护了现在的点集Q中的点通过一次移动可以到达任意点集Q以外的点,且所选点集Q中编号小的点与编号大的点之间没有连边。
- 第二层for循环:从大到小枚举点的编号,这次循环删除了编号大的到编号小的点之间的边,然后就可以严格保证点集内部没有边了。
这时,我们考虑另一个约束条件: 点集Q内的点通过1次或2次移动,到达Q集合以外的任意一个点!
我们现在可以发现,对于已经删除你的点,我们可通过将它删除的那个点到达它及与它相连的不在点集中的点(满足 1<=移动次数<=2)
代码如下:
#include <bits/stdc++.h>
#define ll long long
#define N 1000007
using namespace std;
int n,m,vis[N],cnt;
vector<int>V[N];
int main()
{
scanf("%d%d",&n,&m);
int x,y;
for(int i = 1;i <= m;i++)
{
scanf("%d%d",&x,&y);
V[x].push_back(y);
}
for(int i = 1;i <= n;i++)
{
if(!vis[i])
{
vis[i]=1;
for(auto v : V[i]) //C++11,请食用
{
if(!vis[v]) vis[v]=-1;
}
}
}
for(int i = n;i >= 1;i--)
if(vis[i]==1)
{
cnt++;
for(auto v : V[i]) vis[v]=-1;//C++11,请食用
}
printf("%d\n",cnt);
for(int i = 1;i <= n;i++)
{
if(vis[i]==1) printf("%d ",i);
}
puts("");
}