CodeForces - 1368E Ski Accidents(拓扑排序)

题目链接:点击查看

题目大意:给出一张 n 个点和 m 条边组成的有向无环图,规定:每个点的出度一定小于等于 2 ,现在要求删除掉至多 \frac{4}{7}n 个点,使得任意一条路径的长度都不大于 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;
}

猜你喜欢

转载自blog.csdn.net/qq_45458915/article/details/106860890
ski
今日推荐