不得不说,这是道很难减少时间复杂度的题,且这个题有点像一道拓扑排序题,但是这个难度标签有点低。
我们应该可以想到拓扑排序可能是这个题的正解,但是题目中有输出总数,因此我们就可以造一个数组表示从这个点出发向下有几个食物链,然后最后再输出每个入度为零且出度不为零的点所记忆化搜索到的点的个数。
我们先上70分的代码
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; struct zzz{ int t,nex; }e[200010<<1]; int head[100010],tot; void add(int x,int y){ e[++tot].t=y; e[tot].nex=head[x]; head[x]=tot; } int in[100010],out[100010],vis[100010],ans; int dfs(int x){ if(vis[x]) return vis[x]; for(int i=head[x];i;i=e[i].nex){ if(out[e[i].t]==0) vis[e[i].t]++; else vis[x]+=dfs(e[i].t); } } int main(){ int n,m; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int x,y; scanf("%d%d",&x,&y); in[y]++; out[x]++; add(x,y); } for(int i=1;i<=n;i++){ if(in[i]==0&&out[i]){ dfs(i); } ans+=vis[i]; } cout<<ans; return 0; }
我们看这个代码觉得这不是加记忆化搜索了吗,为什么还是不够快呢。
因为这个记忆化他只加了一个,就是第一个,可是万一这一个始终实现不了,那就GG了,所以我们应该再加两个,即在每搜索到一个出度为0的点就返回1,且我们应该在遍历完这个点的所有边的时候加完之后再return一个。
所以做完这个题我们应该有了一个教训,便是记忆化搜索能加就加,别吝啬。
代码;
// luogu-judger-enable-o2 #include<bits/stdc++.h> using namespace std; struct cym{ int u; int v; int next; }e[200100]; int head[100100],in_degree[100001],out_degree[100010],ans,total,dp[100010];//dp数组表示i这个点向下有几个出度为零的点,也就是有几条食物链 void add(int a,int b) { e[++total].u=a; e[total].v=b; e[total].next=head[a]; head[a]=total; } int dfs(int x) { int num=0; if(dp[x]) return dp[x]; if(!out_degree[x]) return 1;//如果出度为零,就直接加1; for(int i=head[x];i;i=e[i].next) num+=dfs(e[i].v);//向下不断遍历,加上以x为起点的边其终点的dp数组的值 dp[x]=num; return dp[x]; } int main() { int n,m,u,v; scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d",&u,&v); add(u,v); in_degree[v]++; out_degree[u]++;//记录出入度,别记反了。 } for(int i=1;i<=n;i++) if(!in_degree[i]&&out_degree[i]) ans+=dfs(i);//有几个入度为零的点就算几次食物链,比如样例就只算算了一次 cout<<ans; } //@4017
代码后面有彩蛋。