洛谷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\n",ans);
return 0;
}