题面链接:https://vjudge.net/contest/478160#problem/G
input
10 16
1 2
1 4
1 10
2 3
2 5
4 3
4 5
4 8
6 5
7 6
7 9
8 5
9 8
10 6
10 7
10 9
output
9
数据范围
1≤N≤100000,0≤m≤200000
保证答案不会超过 int 的最大值
思路:我们首先应该在纸上模拟一边样例,这样可以更好地理解题意,也可以为我们写程序 提供思路。
在纸上模拟后,我们明白题目所求的是入度为0的点到出度为0的点的方案数。再看数据范围,直接暴搜肯定会超时,所以我们可以用记忆化搜索。
d[i]记录从点 i 出发到出度为0的总方案数,当搜索至 i 时,直接返回d[i]的值,实现记忆化。
另外有两处要注意的点
1)如何处理单个的点(即入度,出度均为0),依题意,单个点不能构成食物链,所以我们在更新ans时,应注意只更新入度为0,出度不为0的点。
2)使用vector存邻接表时,应先存入一个元素,这样在后面遍历邻接表时,才不会因为 i = 0,实际上要更新,而未更新。
(Ps, 这道题提醒了我们,一定要注意样例的实现,在生物中,其实一条食物链,一定要有生产者,但是可以不以最高级捕食者结尾;
还有就是考虑思考问题的方式,我最开始将问题翻译为求f[i][j](即入度为0的 i 点到出度为0的 j 点)的和,这就导致我们很难使用记忆化搜索,但是如果将问题翻译为求d[i],则记忆化搜索可以迎刃而解)
具体参考代码实现
AC代码:
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std;
typedef long long LL;
const int N = 1e5;
int fir[N + 5], ind[N + 5], outd[N + 5], d[N + 5];
int n, m, ans;
vector <int> nex, ver;
inline LL read()
{
char ch; bool flag = 0;
while((ch = getchar()) < '0' || ch > '9')
if(ch == '-') flag = 1;
LL res = ch - '0';
while((ch = getchar()) >= '0' && ch <= '9')
res = (res << 1) + (res << 3) + ch - '0';
return flag ? -res : res;
}
void add(int x, int y)
{
nex.push_back(fir[x]); fir[x] = nex.size() - 1; ver.push_back(y);
}
int dfs(int x)
{
if(d[x]) return d[x];
if(!outd[x]) return 1;
for(int i = fir[x], v; v = ver[i], i; i = nex[i])
{
d[x] += dfs(v);
}
return d[x];
}
int main()
{
n = read(); m = read();
nex.push_back(0); ver.push_back(0);
for(int i = 1; i <= m; ++i)
{
int x, y;
x = read(); y = read();
add(x, y);
++ind[y]; ++outd[x];
}
for(int i = 1; i <= n; ++i)
{
if(!ind[i] && outd[i])
ans += dfs(i);
}
printf("%d\n", ans);
return 0;
}```
本文解析了如何使用记忆化搜索算法解决Vjudge平台上的一个问题,涉及寻找从入度为0的节点到出度为0的节点的路径数量。通过模拟样例和理解题意,博主强调了处理单点特性和邻接表存储的重要性。代码实例展示了如何利用vector和dfs函数实现高效求解。
2022&spm=1001.2101.3001.5002&articleId=123108919&d=1&t=3&u=e75b29075b254db39b8667f3077f1cee)
347

被折叠的 条评论
为什么被折叠?



