根据题意我们不难理解该题的意思就是求出一个图中的食物链一共有多少条,而我们知道食物链的起点是生产者不会捕食其他生物,终点时不会被捕食的捕食者,仔细想我们会发现,生产者的入度为0,而不会被捕食的捕食者的出度为0,从入度为0的点开始,这不是拓扑排序的特点吗,因此考虑使用拓扑排序
如上图所示,以蓝色点为起点,红色点为终点,我们不难发现,到达红色点路径数取决于到达与之相连的2、3、4点,而到达它们的路径数又取决于与之相连的点,以此类推
我们假设起点的路径数为1,那么在拓扑排序的过程中,我们只需将与当前相连的点的路径数+=当前点的路径数即可
第一轮:删除 1 号蓝色点,1 号蓝色点可以到的点(2 号点、3 号点)都加 1
第二轮:删除 2 号点,2 号点可以到的点(3 号点、5 号红色点)都加 1。此时 3 号点答案为2,5 号点答案为 1
第三轮:删除 3 号点,3号点可以到的点(4 号点、5 号红色点)都加 2。此时 5 号点答案为 3,4 号点答案为 2
第四轮:最后删除 4 号点,4 号点可以到的点(5 号红色点)加 2,此时 5 号点答案为 5
可见全图只有 5 号一个红色点,那么答案就是 5 号点的答案———— 5 了
那么代码实现就很简单了!(题解参考自)
#include<iostream> #include<vector> #include<stack> using namespace std; const int MAX=5e4,mod=80112002; vector<int>mapp[MAX]; int arr1[MAX];//记录各个顶点的入度 int arr2[MAX];//出度 int ans[MAX];//以ans[i]为终点的食物链数 int n,m; void fun(){ stack<int>stk; for(int i=1;i<=n;i++){ if(arr1[i]==0){ stk.push(i);//将入度为0的点入栈 ans[i]=1; } } while(!stk.empty()){ int cur=stk.top(); stk.pop(); int sizee=mapp[cur].size(); for(int i=0;i<sizee;i++){ int v=mapp[cur][i]; ans[v]=(ans[v]+ans[cur])%mod;//注意取模 arr1[v]--; if(arr1[v]==0){//入度为0 stk.push(v); } } } } int main(){ int res=0; cin>>n>>m; for(int i=1;i<=m;i++){ int x,y; cin>>x>>y; mapp[x].push_back(y); arr1[y]++;//入度 arr2[x]++; //出度 } fun(); for(int i=1;i<=n;i++){ if(arr2[i]==0){ res=(res+ans[i])%mod;//注意可能有多个出度为0的点 } } cout<<res<<endl; return 0; }
当然像这种求解从起点开始到终点有多少条路径的题目也可以使用DFS来做
只需循环遍历起点进行DFS,只要到达一次终点就ans++即可
#include<iostream> #include<vector> using namespace std; const int MAX=5e4; vector<int>mapp[MAX]; int arr1[MAX];//记录各个顶点的入度 int arr2[MAX];//出度 int n,m,ans; void dfs(int cur){ if(arr2[cur]==0){//终点为出度为0的点 ans++; return ; } int sizee=mapp[cur].size(); for(int i=0;i<sizee;i++){ dfs(mapp[cur][i]); } } int main(){ cin>>n>>m; for(int i=1;i<=m;i++){ int x,y; cin>>x>>y; mapp[x].push_back(y); arr1[y]++; arr2[x]++; } for(int i=1;i<=n;i++){ if(arr1[i]==0){//从入度为0的顶点开始遍历 dfs(i); } } cout<<ans<<endl; return 0; }
但是该题边的数量很多,因此会超时
我们考虑用记忆化搜索进行优化
#include<iostream> #include<vector> #include<stack> using namespace std; const int MAX=5e4,mod=80112002; vector<int>mapp[MAX]; int arr1[MAX];//记录各个顶点的入度 int arr2[MAX];//出度 int book[MAX];//记忆数组,记录从i到终点有多少条路径 int n,m; int dfs(int cur){ if(arr2[cur]==0)return 1;//出度为0的点 if(book[cur])return book[cur];//记忆 int sizee=mapp[cur].size();//临接边数 int sum=0; for(int i=0;i<sizee;i++){ sum=(sum+dfs(mapp[cur][i]))%mod; } book[cur]=sum; return book[cur]; } int main(){ int res=0; cin>>n>>m; for(int i=1;i<=m;i++){ int x,y; cin>>x>>y; mapp[x].push_back(y); arr1[y]++;//入度 arr2[x]++; //出度 } for(int i=1;i<=n;i++){ if(arr1[i]==0){ res=(res+dfs(i))%mod;//注意可能有多个入度为0的点 } } cout<<res<<endl; return 0; }