P3183 [HAOI2016]食物链(记忆化搜索/拓扑排序)

# [HAOI2016]食物链

## 题目描述

 

如图所示为某生态系统的食物网示意图,据图回答第1小题现在给你n个物种和m条能量流动关系,求其中的食物链条数。物种的名称为从1到n编号M条能量流动关系形如a1 b1a2 b2a3 b3......am-1 bm-1am bm其中ai bi表示能量从物种ai流向物种bi,注意单独的一种孤立生物不算一条食物链

## 输入格式

第一行两个整数n和m,接下来m行每行两个整数ai bi描述m条能量流动关系。(数据保证输入数据符号生物学特点,且不会有重复的能量流动关系出现)1<=N<=100000 0<=m<=200000题目保证答案不会爆 int

## 输出格式

一个整数即食物网中的食物链条数

## 样例 #1

### 样例输入 #1

```
10 16
1 2
1 4
1 10
2 3
2 5
4 3
4 5
4 8
6 5
7 6
7 9
8 5
9 8
10 6
10 7
10 9
```

### 样例输出 #1

```
9
```

记忆化搜索:

从入度为0的点开始往下面节点搜索(切记入读为0并且出度为0是个单独的节点pass掉),直到搜索到出度为0的节点,返回1,途中记录每个节点是否被搜索遍历过,如果被遍历过,直接返回权值即可。

从入读为零的点进行记忆化搜索,搜到出度为零的点返回1

所有返回值加起来就是答案。

#include<bits/stdc++.h>
using namespace std;
struct node {
	int u,v,next;
} e[200010];
int st[1000010];
int out[1000010];
int ind[1000010];
int f[1000010];
int n,m,a,b,ans,tot,s,end;
void add(int x,int y) {
	e[++tot].u=x;
	e[tot].v=y;
	e[tot].next=st[x];
	st[x]=tot;
}
int dfs(int x) {
	if(f[x])return f[x];//如果之前记录过该点有几条到达过他的方案
	int ans=0;
	if(out[x]==0)return 1;//到达了初读为0的地方,也就是到达了最后
	for(int i=st[x]; i; i=e[i].next) {
		ans+=dfs(e[i].v);
	}
	f[x]=ans;//记忆化,x可以有几条食物链
	return ans;
}
int main() {

	scanf("%d%d",&n,&m);
	for(int i=1; i<=m; i++) {
		scanf("%d%d",&a,&b);
		add(a,b);
		out[a]++;//a的初读+1
		ind[b]++;//b的入读+1
	}
	for(int i=1; i<=n; i++) {
		if(ind[i]==0&&out[i]!=0) {
			//入读为0出度不为0
			ans+=dfs(i);
		}
	}
	cout<<ans;
}

拓扑排序 :我们记录入度为0并且出度不为0的点方案书为1,然后直接裸toposort,最后的答案就是所有加入队列的出度为0的点

#include<bits/stdc++.h>
#include<queue>
using namespace std;
const int N=2e6+5;
int rd[N];
int n,m,f[N];
vector<int>e[N];//邻接矩阵存储,速度慢于上面的链式前向星存储结构
int toposort() {
	queue<int>q;
	int ans=0;
	for(int i=1; i<=n; i++) {
		if(!rd[i]&&e[i].size()) { //入读为0并且出度不为0
			q.push(i);
			f[i]=1;
		}
	}
	while(!q.empty()) {
		int x=q.front();
		q.pop();
		if(!e[x].size())//出度为0
			ans+=f[x];
		for(auto t: e[x]) {
			f[t]+=f[x],rd[t]--;
			if(!rd[t]) { //入读为0了
				q.push(t);
			}
		}
	}
	return ans;
}
int main() {
	cin>>n>>m;
	for(int i=1,x,y; i<=m; i++) {
		cin>>x>>y;
		rd[y]++;
		e[x].push_back(y);
	}
	cout<<toposort();
}

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值