当前位置: 首页 > news >正文

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)

文章目录 题目题目翻译深度优先搜索&#xff08;dfs&#xff09;宽度优先搜索&#xff08;bfs&#xff09;总结 原题链接 题目 题目翻译 微博被称为中国的推特。在微博上&#xff0c;一个用户可能有很多粉丝&#xff0c;也可能关注许多其他用户。因此&#xff0c;通过粉丝关系…...

揭开 gRPC、RPC 、TCP和UDP 的通信奥秘

差异点 特性TCPUDPRPCgRPCHTTP工作层级传输层传输层应用层应用层应用层传输协议面向连接的传输协议无连接传输协议使用 TCP、HTTP 等协议HTTP/2HTTP/1.1, HTTP/2序列化格式字节流数据报文XML、JSON 或自定义Protocol BuffersJSON 或 XML特点可靠的连接传输无连接、快速传输远程…...

使用Web Worker来处理多线程操作,以及如何避免主线程卡顿。

在JavaScript中处理大量数据时&#xff0c;由于JavaScript是单线程的&#xff0c;所有的操作都在主线程上运行&#xff0c;因此处理大量数据可能导致页面卡顿和响应迟缓。为了避免这些问题&#xff0c;可以使用Web Workers来实现多线程操作&#xff0c;允许在后台线程中处理复杂…...

杂谈:业务说的场景金融是什么?

引言&#xff1a;市场格局的转变 在供应短缺的年代&#xff0c;是典型的卖方市场。为了保证稳定供货&#xff0c;买方会提前一段时间下单&#xff0c;也几乎没什么议价能力。卖方只需等着接单就行。 现在很多领域的供应商数量越来越多&#xff0c;而且随着互联网的普及&#…...

在vscode实现用和Chrome开发者工具中相同的快捷键进行面板切换

在Chrome开发者工具中&#xff0c;我们可以用 Ctrl [ 和 Ctrl ] 快捷键来切换面板&#xff0c;用起来很方便。 vscode中默认没有这两个快捷键&#xff0c;我们可以通过配置自定义快捷键来实现相同的功能。 配置方法&#xff1a; 1. 按 Ctrl K, Ctrl S 调出快捷键配置面板。…...

【ESP32+MicroPython】硬件控制基础

ESP32是一款功能强大的微控制器&#xff0c;具有多种硬件接口。本文以“ESP32硬件控制”为主题&#xff0c;逐步介绍GPIO&#xff08;通用输入输出&#xff09;、PWM&#xff08;脉宽调制&#xff09;、ADC&#xff08;模数转换&#xff09;等功能的原理与实现&#xff0c;并结…...

Python学习从0到1 day26 第三阶段 Spark ① 数据输入

要学会 剥落旧痂 然后 循此新生 —— 24.11.8 一、Spark是什么 定义&#xff1a; Apache Spark 是用于大规模数据处理的统一分析引擎 简单来说&#xff0c;Spark是一款分布式的计算框架&#xff0c;用于调度成百上千的服务器集群&#xff0c;计算TB、PB乃至EB级别的海量数据…...

kafka消费者的消费分区策略有哪些,默认是哪个?

Kafka消费者的分区分配策略主要有以下几种&#xff0c;分别决定了如何将多个分区分配给消费者&#xff1a; 1. Range&#xff08;范围分配&#xff09; 描述&#xff1a;将分区连续地分配给消费者。每个消费者负责一段连续的分区。如果有多个消费者&#xff0c;那么消费者会按…...

前端常用时间操作汇总

&#xff08;1&#xff09;获取中国标准时间&#xff1a; let now new Date(); ​ // Thu Nov 14 2024 17:13:49 GMT0800 (中国标准时间) &#xff08;2&#xff09;获取年份&#xff1a; let year now.getFullYear(); ​ // 2024 &#xff08;3&#xff09;获取月份&…...

106. UE5 GAS RPG 使用MVVM

MVVM 是 Model-View-ViewModel的缩写&#xff0c;个人理解它和MVC很相似&#xff0c;有区别的地方在于&#xff0c;在MVC里&#xff0c;Controller会服务多个View&#xff0c;而MVVM里&#xff0c;每个View都拥有一个单独的ViewModel&#xff0c;所以ViewModel相当于精简版的Co…...

Elasticsearch中什么是倒排索引?

倒排索引&#xff08;Inverted Index&#xff09;是一种索引数据结构&#xff0c;它在信息检索系统中被广泛使用&#xff0c;特别是在全文搜索引擎中。倒排索引允许系统快速检索包含给定单词的文档列表。它是文档内容&#xff08;如文本&#xff09;与其存储位置之间的映射&…...

深度学习:AT Decoder 详解

AT Decoder 详解 在序列到序列的模型架构中&#xff0c;自回归解码器&#xff08;Autoregressive Translator, AT Decoder&#xff09;是一种核心组件&#xff0c;其设计目标是确保生成的序列在语义和语法上的连贯性与准确性。自回归解码器通过逐步、依赖前一输出来生成新的输…...

pythons工具——图像的随机增强变换(只是变换了图像,可用于分类训练数据的增强)

从文件夹中随机选择一定数量的图像&#xff0c;然后对每个选定的图像进行一次随机的数据增强变换。 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 中的一个类型限定符&#xff0c;它用于告诉编译器被修饰的变量具有特殊的属性&#xff0c;编译器在对该变量进行优化时需要特殊对待。以下是volatile限定符的主要作用&#xff1a; 1. 防止优化 内存访问顺序&#xff1a;在多线程环境或者与硬件交互的程序中…...

如何关闭Python解释器

方法1&#xff1a;采用sys.exit(0)正常终止程序&#xff0c;从图中可以看到&#xff0c;程序终止后shell运行不受影响。 方法2&#xff1a;采用os._exit(0)关闭整个shell&#xff0c;从图中看到&#xff0c;调用sys._exit(0)后整个shell都重启了&#xff08;RESTART Shell&…...

《TCP/IP网络编程》学习笔记 | Chapter 9:套接字的多种可选项

《TCP/IP网络编程》学习笔记 | Chapter 9&#xff1a;套接字的多种可选项 《TCP/IP网络编程》学习笔记 | Chapter 9&#xff1a;套接字的多种可选项套接字可选项和 I/O 缓冲大小套接字多种可选项getsockopt & setsockoptSO_SNDBUF & SO_RCVBUF SO_REUSEADDR发生地址绑定…...

渗透测试---网络基础之HTTP协议与内外网划分

声明&#xff1a;学习素材来自b站up【泷羽Sec】&#xff0c;侵删&#xff0c;若阅读过程中有相关方面的不足&#xff0c;还请指正&#xff0c;本文只做相关技术分享,切莫从事违法等相关行为&#xff0c;本人一律不承担一切后果 目录 一、HTTP协议各版本介绍 二、HTTP请求的方…...

15分钟学 Go 第 45 天 : 使用Docker容器

第45天&#xff1a;使用Docker容器 目标 在本节中&#xff0c;我们将深入了解Docker及其基本用法&#xff0c;掌握如何使用Docker容器来简化开发和部署流程。 背景知识 Docker是一个开源平台&#xff0c;用于开发、运输和运行应用程序。它使我们能够使用容器技术将应用程序…...

DriveLM 论文学习

论文链接&#xff1a;https://arxiv.org/pdf/2312.14150 代码链接&#xff1a;https://github.com/OpenDriveLab/DriveLM 解决了什么问题&#xff1f; 当前&#xff0c;自动驾驶方案的性能仍然不足。一个必要条件就是泛化能力&#xff0c;需要模型能处理未经训练的场景或不熟…...

YoloV10改进策略:上采样改进|CARAFE,轻量级上采样|即插即用|附改进方法+代码

论文介绍 CARAFE模块概述&#xff1a;本文介绍了一种名为CARAFE&#xff08;Content-Aware ReAssembly of FEatures&#xff09;的模块&#xff0c;它是一种用于特征上采样的新方法。应用场景&#xff1a;CARAFE模块旨在改进图像处理和计算机视觉任务中的上采样过程&#xff0…...

Java 8 Stream API 入门到实践详解

一、告别 for 循环&#xff01; 传统痛点&#xff1a; Java 8 之前&#xff0c;集合操作离不开冗长的 for 循环和匿名类。例如&#xff0c;过滤列表中的偶数&#xff1a; List<Integer> list Arrays.asList(1, 2, 3, 4, 5); List<Integer> evens new ArrayList…...

PPT|230页| 制造集团企业供应链端到端的数字化解决方案:从需求到结算的全链路业务闭环构建

制造业采购供应链管理是企业运营的核心环节&#xff0c;供应链协同管理在供应链上下游企业之间建立紧密的合作关系&#xff0c;通过信息共享、资源整合、业务协同等方式&#xff0c;实现供应链的全面管理和优化&#xff0c;提高供应链的效率和透明度&#xff0c;降低供应链的成…...

《Playwright:微软的自动化测试工具详解》

Playwright 简介:声明内容来自网络&#xff0c;将内容拼接整理出来的文档 Playwright 是微软开发的自动化测试工具&#xff0c;支持 Chrome、Firefox、Safari 等主流浏览器&#xff0c;提供多语言 API&#xff08;Python、JavaScript、Java、.NET&#xff09;。它的特点包括&a…...

解锁数据库简洁之道:FastAPI与SQLModel实战指南

在构建现代Web应用程序时&#xff0c;与数据库的交互无疑是核心环节。虽然传统的数据库操作方式&#xff08;如直接编写SQL语句与psycopg2交互&#xff09;赋予了我们精细的控制权&#xff0c;但在面对日益复杂的业务逻辑和快速迭代的需求时&#xff0c;这种方式的开发效率和可…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

GC1808高性能24位立体声音频ADC芯片解析

1. 芯片概述 GC1808是一款24位立体声音频模数转换器&#xff08;ADC&#xff09;&#xff0c;支持8kHz~96kHz采样率&#xff0c;集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器&#xff0c;适用于高保真音频采集场景。 2. 核心特性 高精度&#xff1a;24位分辨率&#xff0c…...

算法笔记2

1.字符串拼接最好用StringBuilder&#xff0c;不用String 2.创建List<>类型的数组并创建内存 List arr[] new ArrayList[26]; Arrays.setAll(arr, i -> new ArrayList<>()); 3.去掉首尾空格...

【分享】推荐一些办公小工具

1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由&#xff1a;大部分的转换软件需要收费&#xff0c;要么功能不齐全&#xff0c;而开会员又用不了几次浪费钱&#xff0c;借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...

使用LangGraph和LangSmith构建多智能体人工智能系统

现在&#xff0c;通过组合几个较小的子智能体来创建一个强大的人工智能智能体正成为一种趋势。但这也带来了一些挑战&#xff0c;比如减少幻觉、管理对话流程、在测试期间留意智能体的工作方式、允许人工介入以及评估其性能。你需要进行大量的反复试验。 在这篇博客〔原作者&a…...

人工智能--安全大模型训练计划:基于Fine-tuning + LLM Agent

安全大模型训练计划&#xff1a;基于Fine-tuning LLM Agent 1. 构建高质量安全数据集 目标&#xff1a;为安全大模型创建高质量、去偏、符合伦理的训练数据集&#xff0c;涵盖安全相关任务&#xff08;如有害内容检测、隐私保护、道德推理等&#xff09;。 1.1 数据收集 描…...