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

[蓝桥杯]对局匹配

对局匹配

题目描述

小明喜欢在一个围棋网站上找别人在线对弈。这个网站上所有注册用户都有一个积分,代表他的围棋水平。

小明发现网站的自动对局系统在匹配对手时,只会将积分差恰好是 K 的两名用户匹配在一起。如果两人分差小于或大于 KK,系统都不会将他们匹配。

现在小明知道这个网站总共有 NN 名用户,以及他们的积分分别是 A1,A2,⋯ANA1​,A2​,⋯AN​。

小明想了解最多可能有多少名用户同时在线寻找对手,但是系统却一场对局都匹配不起来(任意两名用户积分差不等于 KK)?

输入描述

输入描述

第一行包含两个个整数 N,KN,K。

第二行包含 NN 个整数 A1,A2,⋯ANA1​,A2​,⋯AN​。

其中,1≤N≤105,0≤Ai≤105,0≤K≤1051≤N≤105,0≤Ai≤105,0≤K≤105。

输出描述

输出一个整数,代表答案。

输入输出样例

示例

输入

10 0
1 4 2 8 5 7 1 4 2 8

输出

6

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

总通过次数: 2604  |  总提交次数: 4404  |  通过率: 59.1%

难度: 困难   标签: 2017, 国赛, 动态规划

对局匹配问题:动态规划解法详解

🌟 算法思路

本问题要求找到最多用户同时在线,但任意两人积分差不等于K。核心思路是​​分组处理 + 动态规划​​:

  1. ​分组策略​​:将用户按积分模K的余数分成K组(余数0~K-1)
  2. ​组内处理​​:每组内积分值差为K的倍数,避免跨组匹配(差为K的数必同余)
  3. ​链式结构​​:每组内积分排序后,相邻差为K的数形成"冲突链"
  4. ​动态规划​​:在每条冲突链上求解最大独立集(不相邻节点和最大)
    📝 算法步骤
  5. ​特殊处理K=0​​:

    • 直接统计不同积分值数量(每个值只能选1个用户)
    • 例:输入[1,1,2,2] → 不同值{1,2} → 答案=2
  6. ​分组处理(K>0)​​:

    • 创建K个分组桶(余数0~K-1)
    • 遍历每个积分a,放入a % K桶中
    • 例:K=2时,积分[1,3,5]放入余数1桶
  7. ​组内处理​​:

    • 对每组积分排序并合并相同值(记录出现次数)
    • 按积分值差划分连续子链(差=K为连续,否则断开)
    • 例:余数0组[0,2,4,6] → 子链0-2-4-6
  8. ​链上动态规划​​:

    • 状态定义:
      • dp0:不选当前节点的最大和
      • dp1:选择当前节点的最大和
    • 状态转移:
      • 不选当前:new_dp0 = max(dp0, dp1)
      • 选当前:new_dp1 = dp0 + 当前权值
    • 链尾结果:max(dp0, dp1)
  9. ​结果汇总​​:

    • 累加所有组的结果
      🧠 代码实现(C++)
      #include <iostream>
      #include <vector>
      #include <algorithm>
      #include <set>
      using namespace std;int main() {ios::sync_with_stdio(false);cin.tie(0);int N, K;cin >> N >> K;vector<int> A(N);for (int i = 0; i < N; i++) {cin >> A[i];}// 处理K=0的情况if (K == 0) {set<int> distinct;for (int a : A) distinct.insert(a);cout << distinct.size() << endl;return 0;}// 创建K个分组vector<vector<int>> groups(K);for (int a : A) {groups[a % K].push_back(a);}long long ans = 0;// 处理每个分组for (int r = 0; r < K; r++) {if (groups[r].empty()) continue;// 组内排序sort(groups[r].begin(), groups[r].end());// 合并相同积分并计数vector<pair<int, int>> arr; // (积分值, 出现次数)int cnt = 1;for (int i = 1; i < groups[r].size(); i++) {if (groups[r][i] == groups[r][i-1]) {cnt++;} else {arr.push_back({groups[r][i-1], cnt});cnt = 1;}}arr.push_back({groups[r].back(), cnt});// 划分子链(相邻差为K的连续段)vector<vector<pair<int, int>>> chains;vector<pair<int, int>> chain;chain.push_back(arr[0]);for (int i = 1; i < arr.size(); i++) {if (arr[i].first - arr[i-1].first == K) {chain.push_back(arr[i]);} else {chains.push_back(chain);chain = {arr[i]};}}chains.push_back(chain);// 每条子链动态规划for (auto& ch : chains) {if (ch.empty()) continue;long long dp0 = 0;          // 不选前一个节点long long dp1 = ch[0].second; // 选前一个节点for (int i = 1; i < ch.size(); i++) {long long new_dp0 = max(dp0, dp1);long long new_dp1 = dp0 + ch[i].second;dp0 = new_dp0;dp1 = new_dp1;}ans += max(dp0, dp1);}}cout << ans << endl;return 0;
      }
      📊 代码解析
    • ​输入处理​​:

      • 使用ios::sync_with_stdio(false)加速输入
      • 一维数组存储积分值
    • ​K=0特判​​:

      • 利用set去重统计不同积分数量
    • ​分组管理​​:

      • vector<vector<int>> groups(K)创建分组桶
      • 模运算a % K确定分组
    • ​组内压缩​​:

      • 排序后合并相同积分,存储为(值, 频次)
      • 例:[2,2,2,4] → (2,3),(4,1)
    • ​子链划分​​:

      • 相邻积分差=K的划为同链
      • 例:[0,2,4,10](K=2)→ 子链[0,2,4][10]
    • ​动态规划优化​​:

      • 滚动变量dp0/dp1代替DP数组
      • 空间复杂度O(1)每链,时间复杂度O(N)
    • 🧪 实例验证

      ​输入1​​:K=0,积分=[1,4,2,8,5,7,1,4,2,8]

    • 不同值:{1,2,4,5,7,8}
    • 输出:6 ✓
    • ​输入2​​:K=2,积分=[0,2,4,1,3,5,7]
    • 分组:
      • 余0组:[0,2,4] → 子链0-2-4
        • DP:节点(0,1)-(2,1)-(4,1)
        • 选0+4=2
      • 余1组:[1,3,5,7] → 子链1-3-5-7
        • DP:选1+5或3+7=2
    • 输出:2+2=4 ✓
    • ​输入3​​:K=3,积分=[1,1,4,4,7,7,10]

    • 余1组:[1,1,4,4,7,7,10]
      • 合并:(1,2),(4,2),(7,2),(10,1)
      • 子链:1-4-7(差3)和10
      • 链1 DP:选1+7=4
      • 链2 DP:选10=1
    • 输出:4+1=5 ✓
    • ⚠️ 注意事项
    • ​边界处理​​:

      • K=0需单独处理
      • 空分组直接跳过
      • 单元素链直接取权值
    • ​数据类型​​:

      • 结果用long long防溢出(最大1010)
      • 积分值范围0-105,用int存储
    • ​性能关键​​:

      • 排序复杂度O(N log N)
      • 链划分和DP均O(N)
      • 整体复杂度O(N log N)
    • ​特殊测试点​​:

      测试类型测试数据预期结果
      K=0[1,1,2,2]2
      K>最大积分K=100000, [1,2,3]3
      全相同积分K=1, [5,5,5]3
      不连续子链K=2, [0,3,6,10,13]4
      超大随机数据N=100000, K=50000通过1s限
    • 💡 优化建议
    • ​合并相同值优化​​:

      // 使用map避免排序后遍历
      unordered_map<int, int> freq;
      for (int a : groups[r]) freq[a]++;
      for (auto& p : freq) arr.push_back(p);
      sort(arr.begin(), arr.end());
    • ​链划分与DP合并​​:

      long long chainDP = 0;
      long long prev0 = 0, prev1 = freq[arr[0]];
      for (int i = 1; i < arr.size(); i++) {if (arr[i] - arr[i-1] == K) {// DP计算...} else {chainDP += max(prev0, prev1);prev0 = 0; prev1 = freq[arr[i]];}
      }
    • ​内存优化​​:

      • vector<vector<int>>().swap(groups)及时释放内存
      • 使用reserve()预分配分组空间
    • ​并行化潜力​​:

      • 不同分组可并行处理
      • 使用OpenMP加速:
        #pragma omp parallel for reduction(+:ans)
        for (int r=0; r<K; r++) { ... }

相关文章:

  • FreeRTOS、Zephyr、RT-Thread,区别与联系
  • 回归分析-非线性回归及岭回归.docx
  • SIPp:SIP 协议性能测试工具!全参数详细教程!Kali Linux教程!
  • 瀚文机械键盘固件开发详解:HWKeyboard.cpp文件解析与应用
  • 物联网控制技术期末复习 知识点总结 第二章 单片机
  • 34.1STM32下的can总线实现知识(区分linux)_csdn
  • java判断一个字符串(如 str1)是否在给定的一组字符串
  • linux如何配置wifi连接
  • 微信小程序开发一个自定义组件的详细教程
  • LLM应用开发(九)- 幻觉及如何缓解
  • gateway 网关 路由新增 (已亲测)
  • HarmonyOS 实战:给笔记应用加防截图水印
  • EMCC 13c 报错 “Metrics Global Cache Blocks Lost is at XXX“ 解决
  • 大语言模型备案与深度合成算法备案的区别与联系
  • yoloe优化:可支持点提示进行检测分割
  • Python训练第四十四天
  • Windows系统下npm报错node-gyp configure got “gyp ERR“解决方法
  • Kafka消息队列笔记
  • 10. MySQL索引
  • Windows系统工具:WinToolsPlus 之 SQL Server Suspect/质疑/置疑/可疑/单用户等 修复
  • 新疆建设职业技术学院校园网站/网站引流推广
  • 网站建设所需资料及费用/阿里指数查询官网入口
  • app跟网站的区别是什么/友情链接平台网站
  • 网站制作与建设教程下载/购物网站排名
  • 石家庄建设项目公示网/抖音seo供应商
  • 网站建设智能优化/企业网站搜索引擎推广方法