洛谷P3183 [HAOI2016]食物链

(dp)太菜了,紧急补课。就从入门题开始吧。。。

思路

其实这题我第一眼是没想到(dp)的,一下就看出是拓扑排序后简单递推了(递推也算(dp)吧。。。),切了。然后考虑到自己不会记忆化搜索,所以才学一波。

“记搜好就好在可以自动找出拓扑序”————zhx

那么既然这题可以用拓扑排序做,当然也就可以使用记搜。其实在递归入门题斐波那契数列的优化中就使用过记搜,用数组记录这个数有没有递归过,如果递归过直接返回值就好了。好像也把这题做完了。但是代码实现还是有一些细节的,而且我也终于理解了为什么记搜不用找拓扑序。因为就算不是按照拓扑序搜的,搜完之后,当搜到起点的时候,由于它后面的点已经搜完了,所以直接返回数组就行了,也就相当于是从起点开始按照拓扑序搜的了。

代码(剽窃后自己打了一遍)

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<ctime>
using namespace std;
typedef long long ll;
const int N=100005,M=200005;
int n,m,tot,ans;
int ver[M],Next[M],head[N],f[N],in[N],out[N];
void add(int x,int y){
	ver[++tot]=y;
	Next[tot]=head[x];
	head[x]=tot;
}
int dfs(int x){
	if(f[x]) return f[x];//记搜的精髓所在,不然就和爆搜没区别了
	int sum=0;//sum用来统计从这个点出发能到达终点的路径条数
	if(out[x]==0) return 1;//如果到达了终点,那么返回1,说明可以到达
	for(int i=head[x];i;i=Next[i]){
		sum+=dfs(ver[i]);//路径条数累加
	}
	f[x]=sum;//f[x]记录从点x出发能到达终点的路径条数
	return sum;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=1,x,y;i<=m;i++){
		scanf("%d%d",&x,&y);
		add(x,y);
		in[y]++;
		out[x]++;
	}
	for(int i=1;i<=n;i++){
		if(in[i]==0&&out[i]!=0){
			ans+=dfs(i);
		}
	}
	printf("%d
",ans);
	return 0;
}
原文地址:https://www.cnblogs.com/57xmz/p/13907222.html