PAT甲级 1076 Forwards on Weibo(30)
文章目录
- 题目
- 题目翻译
- 深度优先搜索(dfs)
- 宽度优先搜索(bfs)
- 总结
原题链接
题目


题目翻译
微博被称为中国的推特。在微博上,一个用户可能有很多粉丝,也可能关注许多其他用户。因此,通过粉丝关系形成了一个社交网络。当一个用户在微博上发表帖子时,他/她所有的粉丝都可以查看和转发他/她的帖子,然后这些帖子可以被他们的粉丝再次转发。现在,假设只计算L级的间接粉丝,需要计算任何一个特定用户的最大潜在转发量。
输入格式
每个输入文件包含一个测试用例。对于每个案例,第一行包含两个正整数:N(≤1000),用户的数量;L(≤6),被计算的间接粉丝的级别数。因此,所有用户都被假设编号从1到N。然后是N行,每行的格式如下:
M[i] user_list[i]
其中 M[i](≤100)是用户[i]关注的总人数;user_list[i] 是用户[i]关注的 M[i] 个用户的列表。保证没有人会关注自己。所有数字之间用空格分隔。
然后最后给出一个正整数 K,后面跟着 K 个用户ID用于查询。
输出格式
对于每个用户ID,你需要在一行中打印出这个用户可以触发的最大潜在转发量,假设每个可以看到初始帖子的人都会转发一次,并且只计算L级的间接粉丝。
基本思路
求一个用户的最大潜在转发量,就是一层一层地去找粉丝数,不超过L层。每次一层一层去找粉丝数时我们需要知道当前用户是在第几层?是否转发过?到这里,我一开始想到的是用dfs(深度优先搜索),但是最后一个用例超时了,应该是递归调用太多了,爆栈了。所以得用bfs(宽度优先搜索),将该层的用户编号和该用户的层数用一个队列存储起来,然后遍历每个用户的粉丝编号,如果没有转发过答案就+1,如果当前层数<最大层数L,就将它的粉丝编号和它的层数入队
深度优先搜索(dfs)
思路比较简单,先定义一个用户结构体userID,结构体里面存储vector容器,vector存储关注该用户的粉丝编号。再定义结构体数组user[M],将全部用户编号以及该用户的粉丝编号存储起来。
struct userID {vector<int> m;
}user[M];
再来看递归函数dfs的写法,将当前用户的粉丝编号遍历一遍,vis数组用来标记当前用户是否转发过,如果为false,最大转发量就+1,然后标记当前用户为true。然后再判断该层是否小于最大层数L,如果小于就继续递归该粉丝和下一层层数。
void dfs(int id,int l)
{for (int i = 0; i < user[id].m.size(); i++){if (vis[user[id].m[i]] == false){ans += 1;vis[user[id].m[i]] = true;}if(l<L){dfs(user[id].m[i],l+1);}}
}
完整代码
#include<iostream>
#include<vector>
#include<cstring>
using namespace std;
const int M = 1010;
typedef long long ll;
int N, L, K;
int k[M];
bool vis[M];
struct userID {vector<int> m;
}user[M];
ll ans = 0;
void dfs(int id,int l)
{for (int i = 0; i < user[id].m.size(); i++){if (vis[user[id].m[i]] == false){ans += 1;vis[user[id].m[i]] = true;}if(l<L){dfs(user[id].m[i],l+1);}}
}
int main()
{cin >> N >> L;for(int i=1;i<=N;i++){int u;cin >> u;for (int j = 0; j < u; j++){int x;cin >> x;user[x].m.push_back(i);}}cin >> K;for (int i = 0; i < K; i++){cin >> k[i];}for (int i = 0; i < K; i++){ans = 0;memset(vis, false, sizeof vis);vis[k[i]] = true;dfs(k[i], 1);cout << ans << endl;}return 0;
}
通过评测结果发现只过了90%的样例,最后一个运行超时。可能原因就是递归调用太多,导致栈溢出。

递归搜索dfs不行,可以尝试使用非递归搜索bfs
宽度优先搜索(bfs)
延续上述思路,先创建一个队列,该队列存储的是对组pair<int,int>,对组第一个数是用户编号id,第二个数是当前用户所在的层数
queue<pair<int, int>> q;
q.push({用户编号id,用户所在的层数});
步骤
(1)判断当前队列是否为空,如果不为空,获取队头,
(2)遍历该用户的所有关注者——>如果没有转发过则最大转发量+1(再将该关注者标志为转发过),判断该用户的当前层数是否小于最大层数L,如果小于则将该关注者的编号和所在层数入队,注意:关注者的所在层数要加一
q.push({ user[id].m[i],level + 1 });
将发表帖子的用户编号以及层数(层数为1)入队,再重复步骤(1)(2) 直到队列为空
void bfs(int id)
{queue<pair<int, int>> q;q.push({id,1});while (!q.empty()){auto node = q.front();q.pop();int id = node.first;int level = node.second;for (int i = 0; i < user[id].m.size(); i++){if (vis[user[id].m[i]] == false){ans += 1;vis[user[id].m[i]] = true;}if (level < L){q.push({ user[id].m[i],level + 1 });}}}
}
完整代码
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int M = 1010;
typedef long long ll;
int N, L, K;
int k[M];
bool vis[M];
struct userID {vector<int> m;
}user[M];
ll ans = 0;
void bfs(int id)
{queue<pair<int, int>> q;q.push({id,1});while (!q.empty()){auto node = q.front();q.pop();int id = node.first;int level = node.second;for (int i = 0; i < user[id].m.size(); i++){if (vis[user[id].m[i]] == false){ans += 1;vis[user[id].m[i]] = true;}if (level < L){q.push({ user[id].m[i],level + 1 });}}}
}
int main()
{cin >> N >> L;for(int i=1;i<=N;i++){int u;cin >> u;for (int j = 0; j < u; j++){int x;cin >> x;user[x].m.push_back(i);}}cin >> K;for (int i = 0; i < K; i++){cin >> k[i];}for (int i = 0; i < K; i++){ans = 0;memset(vis, false, sizeof vis);vis[k[i]] = true;bfs(k[i]);cout << ans << endl;}return 0;
}
通过评测结果发现还是只过了90%的样例,报错结果是内存超限,那么可能的原因就是堆内存溢出。

我们仔细看下面的代码可以发现:不管当前用户的关注者是否转发过帖子,只要当前用户的层数小于最大层数L,那么该用户的所有关注者都会入队。实际上是没有必要的,如果该用户的某个关注者已经转发过,再将其入队会造成重复查询(到下一层再查询该关注者的所有关注者时,发现已经全部转发过),使内存消耗更大,从而导致堆内存溢出。
for (int i = 0; i < user[id].m.size(); i++)
{if (vis[user[id].m[i]] == false){ans += 1;vis[user[id].m[i]] = true;}if (level < L){q.push({ user[id].m[i],level + 1 });}
}
那么应该怎么改?——>只有当前用户的关注者没有转发过且当前层数小于最大层数L,才能将关注者编号和下一层层数入队。
for (int i = 0; i < user[id].m.size(); i++)
{if (vis[user[id].m[i]] == false){ans += 1;vis[user[id].m[i]] = true;if (level < L){q.push({ user[id].m[i],level + 1 });}}
}
这种写法相当于剪枝,就是将没有必要搜索而且不影响结果的分支去除掉,这样既提高了搜索效率,也节省了计算资源。
剪枝是一种在算法中减少搜索空间的技术,特别是在解决优化问题和决策问题时使用。它的核心思想是在搜索过程中提前排除那些不可能产生最优解或者不符合特定条件的分支,从而减少不必要的计算和资源消耗。
还有一种剪枝的写法(个人不建议)
for (int i = 0; i < user[id].m.size(); i++)
{//不推荐这种写法,因为可能还要将最高层的用户编号入队列,但是已经判断过最高层的用户是否转发过了
// if (vis[user[id].m[i]] == false && level<=L)
// {
// ans += 1;
// vis[user[id].m[i]] = true;
// q.push({ user[id].m[i],level + 1 });
// }//推荐这种写法if (vis[user[id].m[i]] == false){ans += 1;vis[user[id].m[i]] = true;if (level < L){q.push({ user[id].m[i],level + 1 });}}
}
完整代码
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int M = 1010;
typedef long long ll;
int N, L, K;
int k[M];
bool vis[M];
struct userID {vector<int> m;
}user[M];
ll ans = 0;
void bfs(int id)
{queue<pair<int, int>> q;q.push({id,1});while (!q.empty()){auto node = q.front();q.pop();int id = node.first;int level = node.second;for (int i = 0; i < user[id].m.size(); i++){if (vis[user[id].m[i]] == false){ans += 1;vis[user[id].m[i]] = true;if (level < L){q.push({ user[id].m[i],level + 1 });}}}}
}
int main()
{cin >> N >> L;for(int i=1;i<=N;i++){int u;cin >> u;for (int j = 0; j < u; j++){int x;cin >> x;user[x].m.push_back(i);}}cin >> K;for (int i = 0; i < K; i++){cin >> k[i];}for (int i = 0; i < K; i++){ans = 0;memset(vis, false, sizeof vis);vis[k[i]] = true;bfs(k[i]);cout << ans << endl;}return 0;
}
通过评测结果发现通过了全部样例

个人的另一种写法
此写法直接开辟了二维动态数组,省去了创建结构体和结构体数组,但实际上大同小异。
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;
const int M = 1010;
typedef long long ll;
int N, L, K;
int k[M];
bool vis[M];
vector<vector<int>> vc(M);
ll ans = 0;
void bfs(int id)
{queue<pair<int, int>> q;q.push({id, 1});while (!q.empty()){auto node = q.front();q.pop();int id = node.first;int level = node.second;for (int i = 0; i < vc[id].size(); i++){if (vis[vc[id][i]] == false){ans += 1;vis[vc[id][i]] = true;if (level < L){q.push({vc[id][i], level + 1});}}}}
}
int main()
{cin >> N >> L;for(int i=1;i<=N;i++){int u;cin >> u;for (int j = 0; j < u; j++){int x;cin >> x;vc[x].push_back(i);}}cin >> K;for (int i = 0; i < K; i++){cin >> k[i];}for (int i = 0; i < K; i++){ans = 0;memset(vis, false, sizeof vis);vis[k[i]] = true;bfs(k[i]);cout << ans << endl;}return 0;
}
评测结果显示通过了全部样例

yxc的写法
此写法用邻接表来存储图,需要注意的是一共有1000个点,每个点最多存100条边,所以点数N可以开1e3+10,边数M最多为1e5+10。
#include<iostream>
#include<cstring>
#include<queue>
using namespace std;//点数和边数
const int N=1e3+10,M=1e5+10;int n,l;
int h[N],e[M],ne[M],idx; //用邻接表来存储图
bool st[N]; //状态数组//头节点
void add(int a,int b){e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}int bfs(int start){queue<int> q;memset(st, 0, sizeof st);q.push(start); //队列初始化st[start]=true;int res=0;//l为关注着的层数for(int i=0;i<l;i++){ //i为每层的关注者int sz=q.size();//注意:这个while中sz为每层关注着的人数,所以要暂存while(sz--){auto t=q.front();q.pop();//遍历start的第i层关注者for(int j=h[t];j!=-1;j=ne[j]){auto x=e[j]; //e[j]存放关注者的编号if(!st[x]){q.push(x);st[x]=true;res++;}}}}return res;
}int main(){cin>>n>>l;memset(h,-1,sizeof h);for(int i=1;i<=n;i++){int cnt; //第i名用户关注的总人数cin>>cnt;while(cnt--){int x;cin>>x;add(x,i); //i是x的粉丝,让x指向i}}int m;cin>>m;while(m--){int x;cin>>x;cout<<bfs(x)<<endl;}return 0;
}
评测结果显示通过了全部样例

总结
这道题的英文单词有点难理解,比如:forward:转发,follower:粉丝,indirect:间接地
这些单词需要结合上下文才能慢慢读懂,比如forward很多人会翻译成向前的意思
对于深度优先搜索(dfs)和宽度优先搜索(bfs),我们优先考虑能不能使用bfs+剪枝一次通过,因为dfs的局限性有点大,数据稍微大一点就很容易超时(当然也可以通过dfs+剪枝来降低时间复杂度,但是不确定能不能通过)
到这里就结束啦,对以上内容有异议的欢迎大家来讨论。
相关文章:
PAT甲级 1076 Forwards on Weibo(30)
文章目录 题目题目翻译深度优先搜索(dfs)宽度优先搜索(bfs)总结 原题链接 题目 题目翻译 微博被称为中国的推特。在微博上,一个用户可能有很多粉丝,也可能关注许多其他用户。因此,通过粉丝关系…...
揭开 gRPC、RPC 、TCP和UDP 的通信奥秘
差异点 特性TCPUDPRPCgRPCHTTP工作层级传输层传输层应用层应用层应用层传输协议面向连接的传输协议无连接传输协议使用 TCP、HTTP 等协议HTTP/2HTTP/1.1, HTTP/2序列化格式字节流数据报文XML、JSON 或自定义Protocol BuffersJSON 或 XML特点可靠的连接传输无连接、快速传输远程…...
使用Web Worker来处理多线程操作,以及如何避免主线程卡顿。
在JavaScript中处理大量数据时,由于JavaScript是单线程的,所有的操作都在主线程上运行,因此处理大量数据可能导致页面卡顿和响应迟缓。为了避免这些问题,可以使用Web Workers来实现多线程操作,允许在后台线程中处理复杂…...
杂谈:业务说的场景金融是什么?
引言:市场格局的转变 在供应短缺的年代,是典型的卖方市场。为了保证稳定供货,买方会提前一段时间下单,也几乎没什么议价能力。卖方只需等着接单就行。 现在很多领域的供应商数量越来越多,而且随着互联网的普及&#…...
在vscode实现用和Chrome开发者工具中相同的快捷键进行面板切换
在Chrome开发者工具中,我们可以用 Ctrl [ 和 Ctrl ] 快捷键来切换面板,用起来很方便。 vscode中默认没有这两个快捷键,我们可以通过配置自定义快捷键来实现相同的功能。 配置方法: 1. 按 Ctrl K, Ctrl S 调出快捷键配置面板。…...
【ESP32+MicroPython】硬件控制基础
ESP32是一款功能强大的微控制器,具有多种硬件接口。本文以“ESP32硬件控制”为主题,逐步介绍GPIO(通用输入输出)、PWM(脉宽调制)、ADC(模数转换)等功能的原理与实现,并结…...
Python学习从0到1 day26 第三阶段 Spark ① 数据输入
要学会 剥落旧痂 然后 循此新生 —— 24.11.8 一、Spark是什么 定义: Apache Spark 是用于大规模数据处理的统一分析引擎 简单来说,Spark是一款分布式的计算框架,用于调度成百上千的服务器集群,计算TB、PB乃至EB级别的海量数据…...
kafka消费者的消费分区策略有哪些,默认是哪个?
Kafka消费者的分区分配策略主要有以下几种,分别决定了如何将多个分区分配给消费者: 1. Range(范围分配) 描述:将分区连续地分配给消费者。每个消费者负责一段连续的分区。如果有多个消费者,那么消费者会按…...
前端常用时间操作汇总
(1)获取中国标准时间: let now new Date(); // Thu Nov 14 2024 17:13:49 GMT0800 (中国标准时间) (2)获取年份: let year now.getFullYear(); // 2024 (3)获取月份&…...
106. UE5 GAS RPG 使用MVVM
MVVM 是 Model-View-ViewModel的缩写,个人理解它和MVC很相似,有区别的地方在于,在MVC里,Controller会服务多个View,而MVVM里,每个View都拥有一个单独的ViewModel,所以ViewModel相当于精简版的Co…...
Elasticsearch中什么是倒排索引?
倒排索引(Inverted Index)是一种索引数据结构,它在信息检索系统中被广泛使用,特别是在全文搜索引擎中。倒排索引允许系统快速检索包含给定单词的文档列表。它是文档内容(如文本)与其存储位置之间的映射&…...
深度学习:AT Decoder 详解
AT Decoder 详解 在序列到序列的模型架构中,自回归解码器(Autoregressive Translator, AT Decoder)是一种核心组件,其设计目标是确保生成的序列在语义和语法上的连贯性与准确性。自回归解码器通过逐步、依赖前一输出来生成新的输…...
pythons工具——图像的随机增强变换(只是变换了图像,可用于分类训练数据的增强)
从文件夹中随机选择一定数量的图像,然后对每个选定的图像进行一次随机的数据增强变换。 import os import random import cv2 import numpy as np from PIL import Image, ImageEnhance, ImageOps# 定义各种数据增强方法 def random_rotate(image, angle_range(-30…...
C++中volatile限定符详解
volatile是 C 和 C 中的一个类型限定符,它用于告诉编译器被修饰的变量具有特殊的属性,编译器在对该变量进行优化时需要特殊对待。以下是volatile限定符的主要作用: 1. 防止优化 内存访问顺序:在多线程环境或者与硬件交互的程序中…...
如何关闭Python解释器
方法1:采用sys.exit(0)正常终止程序,从图中可以看到,程序终止后shell运行不受影响。 方法2:采用os._exit(0)关闭整个shell,从图中看到,调用sys._exit(0)后整个shell都重启了(RESTART Shell&…...
《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项
《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项 《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项套接字可选项和 I/O 缓冲大小套接字多种可选项getsockopt & setsockoptSO_SNDBUF & SO_RCVBUF SO_REUSEADDR发生地址绑定…...
渗透测试---网络基础之HTTP协议与内外网划分
声明:学习素材来自b站up【泷羽Sec】,侵删,若阅读过程中有相关方面的不足,还请指正,本文只做相关技术分享,切莫从事违法等相关行为,本人一律不承担一切后果 目录 一、HTTP协议各版本介绍 二、HTTP请求的方…...
15分钟学 Go 第 45 天 : 使用Docker容器
第45天:使用Docker容器 目标 在本节中,我们将深入了解Docker及其基本用法,掌握如何使用Docker容器来简化开发和部署流程。 背景知识 Docker是一个开源平台,用于开发、运输和运行应用程序。它使我们能够使用容器技术将应用程序…...
DriveLM 论文学习
论文链接:https://arxiv.org/pdf/2312.14150 代码链接:https://github.com/OpenDriveLab/DriveLM 解决了什么问题? 当前,自动驾驶方案的性能仍然不足。一个必要条件就是泛化能力,需要模型能处理未经训练的场景或不熟…...
YoloV10改进策略:上采样改进|CARAFE,轻量级上采样|即插即用|附改进方法+代码
论文介绍 CARAFE模块概述:本文介绍了一种名为CARAFE(Content-Aware ReAssembly of FEatures)的模块,它是一种用于特征上采样的新方法。应用场景:CARAFE模块旨在改进图像处理和计算机视觉任务中的上采样过程࿰…...
2026软考高项论文题目预测!十大管理+绩效域双押题(附答题思路)
备考软考高项的同学都知道,论文是决定成败的关键一科。随着2025年绩效域全面上位,论文考核方式已从“单一知识点”升级为“绩效域协同五大过程组联动可量化测量指标”的实战型命题。2026年考什么?如何准备?本文基于近3年命题规律&…...
CES Asia 2026打造低空经济生态圈:从整机到核心部件全链覆盖
北京,2026年3月31日电——低空经济产业正迈向全链协同、规模化落地的关键阶段。CES Asia 2026将于6月10—12日在北京举办,以全产业链覆盖精准供需对接资本赋能为核心,构建从整机到核心部件的完整低空经济生态圈,助力企业一站式打通…...
LaTeX2Word-Equation:学术公式无缝迁移的终极解决方案
LaTeX2Word-Equation:学术公式无缝迁移的终极解决方案 【免费下载链接】LaTeX2Word-Equation Copy LaTeX Equations as Word Equations, a Chrome Extension 项目地址: https://gitcode.com/gh_mirrors/la/LaTeX2Word-Equation 在学术写作与科研工作中&#…...
Phi-4-mini-reasoning推理服务监控:通过webshell日志诊断部署状态方法
Phi-4-mini-reasoning推理服务监控:通过webshell日志诊断部署状态方法 1. 模型简介 Phi-4-mini-reasoning 是一个基于合成数据构建的轻量级开源模型,专注于高质量、密集推理的数据处理。作为Phi-4模型家族的一员,它经过专门微调以提升数学推…...
Tencent Hunyuan3D-1.0日志轮转配置:防止磁盘空间耗尽的日志管理方案
Tencent Hunyuan3D-1.0日志轮转配置:防止磁盘空间耗尽的日志管理方案 【免费下载链接】Hunyuan3D-1 腾讯开源的Hunyuan3D-1项目,创新提出两阶段3D生成方法,实现快速、高质量的文本到3D和图像到3D转换,融合Hunyuan-DiT模型…...
OpenClaw语音交互:Phi-3-mini接入麦克风输入实战
OpenClaw语音交互:Phi-3-mini接入麦克风输入实战 1. 为什么需要语音交互能力 上周我在整理电脑文件时突然想到一个问题:当我的双手被占用时(比如正在做饭或修理设备),如何让OpenClaw帮我执行任务?传统的键…...
Pixel Epic · Wisdom Terminal参数详解:显存配额与智力同步率调优指南
Pixel Epic Wisdom Terminal参数详解:显存配额与智力同步率调优指南 1. 认识像素史诗 智识终端 像素史诗 (Pixel Epic) 是一款基于 AgentCPM-Report 大模型构建的高端研究报告辅助终端。它将枯燥的科研过程转化为一场充满像素美学的RPG冒险,让用户以…...
OpenClaw技能开发入门:为Qwen3-4B定制专属自动化模块
OpenClaw技能开发入门:为Qwen3-4B定制专属自动化模块 1. 为什么需要自定义OpenClaw技能 去年夏天,我接手了一个重复性极高的周报生成工作。每周都要从十几个PDF报告中提取关键数据,整理成固定格式的Excel表格,再转成PPT汇报。当…...
ChatGPT+RMBG-2.0:智能图像处理工作流自动化
ChatGPTRMBG-2.0:智能图像处理工作流自动化 1. 当你还在手动抠图时,有人已经用一句话完成整套流程 上周帮朋友处理一批电商产品图,他花了整整两天时间在Photoshop里一张张抠背景、调边缘、换底色。最后发来消息说:“要是能对着图…...
绝地求生游戏辅助工具:罗技鼠标宏自动化配置指南
绝地求生游戏辅助工具:罗技鼠标宏自动化配置指南 【免费下载链接】logitech-pubg PUBG no recoil script for Logitech gaming mouse / 绝地求生 罗技 鼠标宏 项目地址: https://gitcode.com/gh_mirrors/lo/logitech-pubg 在《绝地求生》这款高强度的战术竞技…...
