P4017 最大食物链计数 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)
一开始写的时候考虑用的是dfs,从生产者开始入队,然后进行一个搜索,搜到一次最后的就答案+1;
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e3+2;
const int mod = 80112002;
int pos[maxn][maxn]; //pos[i][0]是i物种的食物的种类,pos[i][]后面开始列举食物的编号
int ju[maxn]; //判断是否是最强的捕食者(也就是没有天敌),0是无天敌,1是有天敌
long long dfs(int x){ //假设捕食者和被捕食者可以是同一个
queue<int>q;
q.push(x);
long long times = 0;
int now;
while(!q.empty()){
now = q.front();
q.pop();
if(pos[now][0]==0)
times++;
else
for(int i = 1;i<=pos[now][0];i++)
q.push(pos[now][i]);
}
return times;
}
int main(){
int n,m;
long long ans = 0;
scanf("%d%d",&n,&m);
for(int i = 1;i<=m;i++){
int f,s;
scanf("%d%d",&s,&f);
ju[s] = 1;
pos[f][++pos[f][0]] = s;
}
for(int i = 1;i<=n;i++){
if(!ju[i]) //如果没有天敌
{
ans += (dfs(i)%mod);
ans %= mod;
}
}
printf("%d",ans);
}
//这里不能用并查集,因为a可以吃b也可以吃c,b可以吃a也可以吃d这样子,没有办法让他们只归到一类里(身兼数职)
(最开始的思路是并查集,后来发现不行)
这个地方的pos【i】【0】代表着i捕食者有pos【i】【0】个食物,食物储存在pos【i】【】这一行,我自己想的奇怪写法,省了一个一维数组来存个数
但是这种写法爆空间了,仔细想想如果用bfs的话最开始的时候会有非常非常多的单位涌入队列,把队列撑满了,然后没办法看题解,学了一下拓扑排序的写法,拓扑排序的空间节省在于,并不会重复入队
然后因为拓扑排序需要保证不能重复入队(lu【】这个数组可能会重复累加)
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5e3+2;
const int mod = 80112002;
int pos[maxn][maxn]; //pos[i][j]代表i可以被j吃
int ru[maxn];
int chu[maxn]; //出是以该点为起点,入是以该点为终点
int lu[maxn]={}; //储存前面节点路径的个数
queue<int>q;
int main(){
int n,m;
scanf("%d%d",&n,&m);
for(int i = 1;i<=m;i++){
int f,s;
scanf("%d%d",&s,&f);
pos[s][f] = 1;
ru[f]++;
chu[s]++;
}
for(int i = 1;i<=n;i++){
if(ru[i]==0) // 生产者才进入
{
q.push(i);
lu[i] = 1;
}
}
long long ans = 0;
int now;
while(!q.empty()){ //从被捕食到捕食者
now = q.front();
q.pop();
for(int i = 1;i<=n;i++){
if(pos[now][i]==0)
continue;
ru[i]--;
lu[i]+=lu[now];
lu[i]%=mod;
if(ru[i] == 0) {
if (chu[i] == 0) {
ans += lu[i];
ans %= mod;
}
else
q.push(i); //这个地方我们最终的捕食者不会入列,而是在访问父节点的时候就已经计算好了,
// 同时,最外面的if保证了只有当这个点入度为0以后才会入列,避免了重复入列的可能性
// (不加也行,反正最后的捕食者由于没有父节点,整个for循环都是continue)
}
}
}
printf("%lld",ans);
}
//这里不能用并查集,因为a可以吃b也可以吃c,b可以吃a也可以吃d这样子,没有办法让他们只归到一类里(身兼数职)
//拓扑排序的思路:储存每个点的出度和入度,入队的元素进行遍历,
// 找到它的食物并且将食物的路线数目储存到自己身上,再它的出节点遍历到自己的时候,自己又可以贡献上去
//由于拓扑排序不能重复入队的特性,在队列中节约了空间
今天到此为止啦,明天也要好好加油