这道题…看到结果要对80112002取模,我就知道数量很大,可我不知道能有这么大…
首先测试数据生产者和最高级的食物链顶端可能有多个(亲测)
然后我想的是,遍历所有生产者,累加这些生产者到最高级消费者的路径数目,每次加起来就取一次模,害怕数据过大,特地开了long long 结果数据量还是超了,最开始我还以为是我算法写错了,害得我重新检查了一遍。后来我试着在每次dfs都取一下模AC了!!!!,这数据量是有多恐怖。。细思极恐。
思路:我们设dp[i]为以i为起点到达最高级食物链的路径条数
如图所示,dp[i]=dp[j]+dp[k]+dp[w]
我们依旧使用dfs的形式,其实这个dfs展开就是逆拓扑排序
dfs(i) : 返回以i为起点,食物链顶端为终点的路径条数。
对于一个顶点i,遍历其所有的出度节点,更新dp[i]的值,也就是
for(int j=0;j<Graph[i].size();j++)
{
int v=Graph[i][j];
dp[i]=(dp[i]+dfs(v));
}
递归的终止条件是什么呢?很容易想到,当那个顶点的出度等于0的时候,就返回1,也就是遇到高级消费者的时候返回1.
下面是AC代码~
#include <iostream>
#include <cstring>
#include <vector>
using namespace std;
#define Max 5005
vector<int > Graph[Max];
typedef long long int ll;
int d[Max];
void solve(int n);
int dfs(int i); //从顶点i到达最高级消费者的路径条数
ll res=0;
int dp[Max]; //从某一点i到达最高级消费者的路径条数
int main()
{
int n,m;
cin>>n>>m;
int x1,x2;
memset(d,0, sizeof(d));
memset(dp,0, sizeof(dp));
for(int i=1;i<=m;i++)
{
cin>>x1>>x2;
Graph[x1].push_back(x2);//x2吃掉x1
d[x2]=1; //标记有入度的节点
}
solve(n);
cout<<res%80112002<<endl;
return 0;
}
int dfs(int i) //从顶点i到达最高级消费者的路径条数
{
if(Graph[i].size()==0)
{
return 1;
}
if(dp[i])
{
return dp[i];
}
for(int j=0;j<Graph[i].size();j++)
{
int v=Graph[i][j];
dp[i]=(dp[i]+dfs(v))%80112002; //数据量很大。。。
}
return dp[i];
}
void solve(int n)
{
for(int i=1;i<=n;i++)
{
if(!d[i]) //入度为0的节点
{
res=(res+dfs(i))%80112002;
}
}
}
上面其实就是逆拓扑排序,下面还是来一遍正的拓扑
我们设dp[i] 是 以i为终点,生产者为起点的路径条数。
根据分析可得:
dp[i]=dp[j]+dp[k]+dp[w]
所以我们遍历这个图的时候,要把握住,任意一个节点的前驱节点一定是在这个节点之前访问过了。因为任意一个节点的dp值都依赖与前驱节点的dp值。
所以:我们先把入度为0的节点压入队列,顺便把入度为0的节点的dp值设置为1,也就是生产者到自己的路径为1。
每次从队列弹出一个节点j,遍历这个节点j的所有的后继节点v,将这些后继节点的入度值全部-1,d[v]–,入度值减为0(d[v]==0)的v压入队列。每次遍历这些后继节点v的时候,记得更新节点v的dp值
dp[v]=dp[v]+dp[j]
上图中dp[i],从i找到前驱节点j,k,w 比较麻烦,所以用上面这种巧妙的方式,按照拓扑排序的顺序,每次找到其后继节点v的时候就更新v的dp值,这样就可以保证在拓扑序列v之前,一定所有的前驱节点都更新了这个dp[v],所以结果一定是正确的。
下面是AC代码~
#include <iostream>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
#define Max 5005
vector<int > Graph[Max];
typedef long long int ll;
int d[Max];
void f(int n);
bool flag[Max];
ll res=0;
int dp[Max]; //dp[i]:以i为终点,生产者到达i的总的路径条数
int main()
{
int n,m;
cin>>n>>m;
int x1,x2;
memset(d,0, sizeof(d));
memset(dp,0, sizeof(dp));
memset(flag,false, sizeof(flag));
for(int i=1;i<=m;i++)
{
cin>>x1>>x2;
Graph[x1].push_back(x2);//x2吃掉x1
d[x2]++; //标记有入度的节点
flag[x1]=true;
}
f(n);
cout<<res<<endl;
return 0;
}
void f(int n)
{
queue<int > que;
for(int i=1;i<=n;i++)
{
if(!d[i])
{
que.push(i);
dp[i]=1;
}
}
while(!que.empty())
{
int j=que.front();
que.pop();
for(int i=0;i<Graph[j].size();i++)
{
int v=Graph[j][i];
d[v]--;
if(!d[v])
{
que.push(v);
}
dp[v]=(dp[v]+dp[j])%80112002;
}
}
for(int i=1;i<=n;i++)
{
if(!flag[i])
{
res=(res+dp[i])%80112002;
}
}
}