K. Birdwatching——DFS剪枝

点这里

题意: 有n个点和m条有向边,根节点T。输出所有与根节点有直接边,并且不能通过其他路径到达根节点的所有节点。

题解: 最容易能够想到DFS,一开始题意理解偏差了一直没写对,完全修改正确了之后交了TLE。一些细节不知道怎么处理就加了一个tarjan强连通分量的模板。后来再找大佬的题解,发现一些细节处理好了就不需要用到tarjan。

  • 反向建图。合法的路径都是到达根节点的,但是要从根节点开始搜索,自然需要反向建边。
  • 搜索起点。为了处理方便,建边时舍弃u->root的边,将这些直接连接根节点的边插入一个vector中,建完图之后以这里面的点作为起点开始DFS。
  • 剪枝。 解决TLE的关键所在,定义一个set类型的vis[i]记录能搜索到i点的dfs起点。如果某个点的vis中记录这一个以上的dfs起点,说明含有多条路径到达根节点,排除答案,并且下次再搜索到的时候可以直接return。
#include<bits/stdc++.h>
#define bug printf("bug*****************\n")
using namespace std;
typedef long long ll;
inline int read(){
   int s = 0, w = 1;
   char ch = getchar();
   while(ch < '0' || ch > '9'){	if(ch == '-') w = -1; ch = getchar();}
   while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
   return s * w;
}

const int N = 1e5 + 10;
int cnt;
int head[N];
vector<int> V;
vector<int> ans;
set<int> vis[N];
struct edge{	int v, next;} e[N];
void addedge(int u, int v){	e[cnt].v = v, e[cnt].next = head[u], head[u] = cnt++;}

void dfs(int u, int fa){
	if(vis[u].size() > 1 || vis[u].count(fa))	return;
	vis[u].insert(fa);
	for(int i = head[u]; ~i; i = e[i].next){
		int v = e[i].v;
		dfs(v, fa);
	}
}

void run(){
	for(int i = 0; i < N; i++)	head[i] = -1;
	int n = read(), m = read(), t = read();
	for(int i = 0; i < m; i++){
		int v = read(), u = read();
		if(u == t)	V.push_back(v);
		else	addedge(u, v);
	}
	for(int i = 0; i < V.size(); i++)
		dfs(V[i], V[i]);
	for(int i = 0; i < V.size(); i++)
		if(vis[V[i]].size() == 1)
			ans.push_back(V[i]);
	sort(ans.begin(), ans.end());
	printf("%d\n", ans.size());
	for(int i = 0; i < ans.size(); i++)
		printf("%d\n", ans[i]);
		
}
int main(){
	run();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值