首页 PAT-Forwards on Weibo
文章
取消

PAT-Forwards on Weibo

A1076 Forwards on Weibo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstdio>
using namespace std;
const int maxn = 1010;
vector<vector<int>> users(1010);
int n, l, k, forwards;//用户总人数、间接粉丝层数、询问个数、最大转发数
bool isVisited[maxn] = { false };
//回溯三部曲
//1.确认返回类型和参数
void dfs(int user, int level)
{
    //2.终止条件
    if (level > l) return;
    //3.每次回溯的遍历过程
    for (int fan : users[user])//遍历该user的粉丝以及粉丝的粉丝
    {
        if (!isVisited[fan])//如果没有遍历过
        {
            isVisited[fan] = true;//先置为true
            forwards++;
        }
        dfs(fan, level + 1);
    }
}
int main()
{
    cin >> n >> l;
    for (int i = 1; i <= n; i++)
    {
        int _num;
        scanf("%d", &_num);
        for (int j = 0; j < _num; j++)//遍历每个用户关注的人
        {
            int _follows;
            scanf("%d", &_follows);
            users[_follows].emplace_back(i);//给他关注的人的粉丝列表中添加自己
        }
    }
    cin >> k;
    for (int i = 0; i < k; i++)
    {
        int _user;
        cin >> _user;
        //重置最大转发数和visited数组
        forwards = 0;
        fill(isVisited + 1, isVisited + 1 + n, false);
        isVisited[_user] = true;//先把自己设置为true,也就是说自己不能算转发量
        dfs(_user, 1);
        //cout << forwards << endl;
        printf("%d\n", forwards);
    }
}

    这个使用dfs写的,但最后一组数据会超时(换成printf和scanf也超时),而且调试正确也花了段时间,这里面有一些坑:

  • 遍历过的节点就不能继续dfs了吗?错误的,只有level超限了才不能dfs,因为如果最开始以level的层数(因为是深度优先遍历)遍历过一个节点(也就是说visted设置为true了),那么在回溯时,发现了这个节点,但level可能并没有超限,所以尽管遍历过它,仍然需要继续遍历它的邻接表,只是不用再多加一次转发数了

  • 由于dfs最后一组会超时,所以还是采用bfs吧

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#include<iostream>
#include<vector>
#include<queue>
using namespace std;

const int maxn = 1010;
vector<vector<int>> fans(maxn);//每个用户的粉丝列表
vector<int> layer(maxn);//记录每个用户的层数
bool isVisited[maxn] = { false };
int n, l, k;//用户总数、层数、询问次数
//广度优先遍历
int BFS(int user)
{
    queue<int> q;//储存粉丝的队列
    q.push(user);//先把自己加进去
    //设置自己为0层
    layer[user] = 0;
    fill(isVisited + 1, isVisited + 1 + n, false);//重置visited数组
    isVisited[user] = true;//先把自己设置为true
    int forwards = 0;//统计转发数
    while (!q.empty())
    {
        int top = q.front();
        q.pop();
        for (int fan : fans[top])//遍历自己的每一个粉丝
        {
            if (isVisited[fan]) continue;//如果之前遍历过,直接跳过
            layer[fan] = layer[top] + 1;//自己粉丝的层数为自己的+1
            if (layer[fan] <= l)//如果没遍历过,而且所在层数小于等于l
            {
                isVisited[fan] = true;
                forwards++;//转发数+1
                q.push(fan);//将这个粉丝加入队列
            }
        }
    }
    return forwards;
}
int main()
{
    cin >> n >> l;
    for (int i = 1; i <= n; i++)
    {
        int _num;//关注人数
        cin >> _num;
        for (int j = 0; j < _num; j++)
        {
            int _follows;
            cin >> _follows;
            fans[_follows].emplace_back(i);//将自己加入到关注的人的粉丝列表
        }
    }
    cin >> k;
    for (int i = 0; i < k; i++)
    {
        int _user;
        cin >> _user;
        cout << BFS(_user) << endl;
    }
}

    这个BFS还是有点坑的:

  • 关于层数的增加时机,不能在遍历到粉丝的开始就将当前粉丝置为top的层数+1,这样会导致一种情况:更改某个用户所在的层数。什么意思呢?就是说,其实每个用户在整个遍历过程中有且只有一个对应的层数。假设用户A有粉丝B和C,C有粉丝B,一旦在用户C的粉丝列表那里再一次遇到已经遍历过的用户B,如果上来就让B的层数+1,显然就更改了B的层数,那么在遍历B的粉丝的时候,B的粉丝层数就会多1,这样就会导致最终的转发数减少。所以,遇到遍历过的节点,直接continue跳过最方便了。
本文由作者按照 CC BY 4.0 进行授权