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

基于哈希表与差分前缀和解决撒狗粮问题

一 题解

#include<bits/stdc++.h>
using namespace std;
bool is_single(string s , const unordered_map<string , string>& couple_map , const unordered_map<string , int>& pos){auto it_couple = couple_map.find(s);//判断没有情侣的情况if(it_couple == couple_map.end()) return true;string ss = it_couple -> second;auto it_pos = pos.find(ss);//判断情侣不在的情况return it_pos == pos.end();
}
int main(){int n;unordered_map<string , string> couple_map;vector<pair<string, string>> couples;cin >> n;for(int i = 0 ; i < n ; i++){string a , b;cin >> a >> b;couple_map[a] = b;couple_map[b] = a;couples.emplace_back(a , b);}//遍历座位int m;cin >> m;vector<string> seat;unordered_map<string , int> pos;for(int i = 0 ; i < m ; i++){string s;cin >> s;seat.push_back(s);pos[s] = i;}//处理是否是单身狗vector<int> single_arr(m , 0);for(int i = 0 ; i < m ; i++){if(is_single(seat[i] , couple_map , pos)){single_arr[i] = 1;}}//差分记录撒狗粮贡献vector<int> diff(m + 2 , 0);for(auto& p : couples){string s1 = p.first;string s2 = p.second;//检查情侣是否在场auto it1 = pos.find(s1);auto it2 = pos.find(s2);if(it1 == pos.end() || it2 == pos.end()) continue;int i = it1 -> second , j = it2 -> second;if(i > j) swap(i , j);if(j - i == 1){//情侣紧挨着int left = i - 1;if(left >= 0){diff[left]++;diff[left+1]--;}int right = j + 1;if(right < m){diff[right]++;diff[right+1]--;}}else{int a = i + 1 , b = j - 1;diff[a]++;diff[b + 1]--;}}//统计被撒狗粮单身狗unordered_map<string , int> count_map;int cur = 0;for(int i = 0 ; i < m ; i++){cur += diff[i];if(single_arr[i]){count_map[seat[i]] = cur;}}//找到最大被撒次数int max_count = 0;for(auto& p : count_map){if(p.second > max_count){max_count = p.second;}}//收集结果vector<string>result;for(auto& p : count_map){if(p.second == max_count){result.push_back(p.first);}}//排序结果sort(result.begin(), result.end());// 9. 输出结果(首尾无多余空格)for (int i = 0; i < result.size(); ++i) {if (i > 0) cout << " ";cout << result[i];}cout << endl;return 0;
}

二、题目的背景需求

  • 聚会上情侣坐次不同,对单身狗 “撒狗粮” 的范围不同:
    • 情侣相邻:两侧单身狗各被撒 1 次(单点更新);
    • 情侣不相邻:中间所有单身狗各被撒 1 次(区间更新)。
  • 需求:统计被撒狗粮次数最多的单身狗,按 ID 升序输出。
  • 数据规模:情侣对数 N≤5e4,总人数 M≤8e4

三、程序中运用到的差分与前缀和

1 前缀和

前缀和就是前面i个数之和,是一种预处理操作,它通过计算数组的前缀和数组,使得我们可以快速查询任意区间的和
即:sum[i]=num[1]+num[2]+……+num[i]

2 差分

差分是前缀和的逆操作,主要用于高效地对数组的某个区间进行批量增减操作。就是数组中每个数减去前面一个数的差(除第一个为原来的数外)
即:cf[i]=num[i]-num[i-1]

初始为全 0(因初始次数为 0)- 区间[a,b]加 1:d[a]++d[b+1]--- 单点x加 1:等价于区间[x,x],即d[x]++d[x+1]--

3 在题目中的运用场景

若直接遍历更新 “被撒次数”:

  • 情侣不相邻时,区间[i+1,j-1]可能包含上千个元素,每次更新需 O (k) 时间(k 为区间长度),N=5e4 时总复杂度可能达 O (N*M),超时风险极高。
  • 差分将所有更新操作(区间 / 单点)压缩为2 次数组赋值(O (1)),最终通过 1 次前缀和(O (M))还原结果,总复杂度降至 O (N+M)。

三、哈希表和差分协同解题

1. 联动逻辑

步骤依赖知识点核心操作
1. 判断情侣是否在场哈希表(pos)通过pos.find(s1)pos.find(s2)确认情侣双方是否在座位表中,排除无效情侣。
2. 确定更新位置哈希表(pos)pos中获取情侣的座位索引ij,确定区间[i+1,j-1]或单点i-1/j+1
3. 高效更新次数差分(diff 数组)对确定的区间 / 单点执行 O (1) 的差分更新,记录被撒狗粮的变化量。
4. 还原最终次数前缀和遍历 diff 数组计算前缀和,得到每个位置的实际被撒次数,关联到单身狗 ID。

2. 算法时间复杂度分析

  • 哈希表操作:插入、查找均为 O (1),总时间 O (N+M)(N 对情侣,M 个座位);
  • 差分操作:N 对情侣的更新均为 O (1),总时间 O (N);
  • 前缀和操作:遍历 M 个位置,总时间 O (M);
  • 整体复杂度:O (N+M),可轻松处理题目中 N≤5e4、M≤8e4 的大数据量。

四、完整解题流程回顾

  1. 输入处理

    • 读取 N 对情侣,用couple_map存储双向映射,couples保存所有情侣对;
    • 读取 M 个座位 ID,用seat存储顺序,pos存储 ID→座位索引映射。
  2. 标记单身狗

    • 遍历每个座位,调用is_single判断是否为单身狗,用single_arr(0/1 数组)标记。
  3. 差分更新

    • 遍历每对情侣,若双方在场,根据坐次(相邻 / 不相邻)执行差分更新(区间 / 单点)。
  4. 前缀和统计

    • 计算 diff 数组的前缀和,得到每个座位的被撒次数,用count_map存储单身狗 ID→被撒次数。
  5. 结果处理

    • 找到count_map中的最大次数,收集所有对应 ID,按字典序(即 ID 升序)排序后输出。

五、常见错误与避坑点

  1. 向量越界

    • 初始seat向量时,若直接cin >> seat[i](未push_back),会因向量为空导致越界;需用seat.push_back(s)动态添加元素。
    • 差分数组diff大小需为m+2,避免d[b+1](如b=m-1b+1=m)越界。
  2. 哈希表查找遗漏

    • 判断情侣是否在场时,需同时检查s1s2是否在pos中,仅一方在场时不执行更新。
  3. 差分更新边界判断

    • 情侣相邻时,更新i-1需判断left>=0,更新j+1需判断right<m,避免越界更新。
  4. 结果排序

    • 单身狗 ID 为 5 位字符串,直接用sort(result.begin(), result.end())即可按 ID 升序排序(字符串字典序与数字升序一致)。
http://www.dtcms.com/a/393636.html

相关文章:

  • 基于多设计模式的状态扭转设计:策略模式与责任链模式的实战应用
  • 残差分析:数据驱动下线性模型的“体检师”与优化指南
  • gorm速成
  • 模型和策略:风控体系的“左右手”之争
  • Keil5 5.38版本在使用STLINK时闪退
  • win11 安装 WSL2 Ubuntu 并支持远程 SSH 登录
  • 基分析积分法则
  • 【Linux】网络——HTTP协议,HTTPS加密
  • HarmonyOS动画:属性动画、显示动画、转场动画
  • Redis 持久化机制详解:RDB 与 AOF 原理与实践
  • 【嵌入式协议外设篇】-8×8 点阵屏
  • 【C++:STL】深入详解string类(一):从读文档开始
  • 电商项目实战总结
  • 22.元类、静态鸭子类型、抽象基类
  • 【论文速递】2025年第21周(May-18-24)(Robotics/Embodied AI/LLM)
  • Android 自定义电池组件(BatteryView)
  • 基于 Stripe/Metering 的用量计费:从 SLO 指标到账单流水
  • 如何解决 pip install 安装报错 ModuleNotFoundError: No module named ‘fastapi’ 问题
  • 论文阅读——隧道中毫米波MIMO信道特性的实验研究
  • The Library: 1靶场渗透
  • 23种设计模式之【装饰器模式】-核心原理与 Java实践
  • 动态规划中的背包问题:0/1 背包与完全背包的核心解析
  • PHP应用-组件框架前端模版渲染三方插件富文本编辑器CVE审计(2024小迪安全DAY30笔记)
  • uniapp 如何判断发的请求是网络异常uni.request
  • 学习:uniapp全栈微信小程序vue3后台 (25)
  • 23种设计模式之【原型模式】-核心原理与 Java实践
  • Netty 重放解码器ReplayingDecoder揭秘:重写轻量异常机制 和 ConstantPool
  • getgeo 生物信息 R语言 表型信息表”“样本信息表”或“临床信息表 phenodata phenotype data
  • OceanBase备租户创建(二):通过BACKUP DATABASE PLUS ARCHIVELOG
  • Linux文件打包压缩与软件安装管理完全指南