Codeforces Round 509 (Div. 2) C. Coffee Break

题目大意:

给你n、m、d

n为元素个数,m为数列长度,d为每个元素之间的最短间隔

问最少需要多少个数列可以使得元素都能装进数列,并且满足每个元素之间的间隔大于等于d

核心思想

使用贪心的思想,将元素的大小进行排序,问题出在必须要优化,不能直接模拟,否则会超时

思路1

使用vector和二分进行处理,

用nums记录原数组

维护一个order动态数组记录原数组从小到大的数分别对应的下标是什么

放置策略是按order数组中%k余数相同的元素放到一个数列,即1,2,...,k,1,2,...k

然后用二分假设多少个数组可以满足要求,其中二分的判定条件就是
n u m s [ o r d e r [ i ] ] − n u m s [ o r d e r [ i − m ] ] < = d nums[order[i]]-nums[order[i-m]]<=d nums[order[i]]−nums[order[i−m]]<=d

m代表二分的中间值,这里是判断原数组中第i大的数-第i-m大的数之间的间隔是否大于等于d

满足后使用map将%k余数相同的元素放到同一组,最后按照顺序从小到大输出即可

cpp 复制代码
// Author: zengyz
// 2025-06-07 10:59

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;

const int N = 2e5 + 10;

void solve()
{
  int n, m, d;
  cin >> n >> m >> d;
  vector<int> nums(n);
  for (auto &v : nums)
    cin >> v;
  vector<int> order(n);
  iota(order.begin(), order.end(), 0);
  sort(order.begin(), order.end(), [&](int i, int j)
       { return nums[i] < nums[j]; });
  int l=1,r=n;
  while(l<=r)
  {
   int m = (l + r) / 2;
    bool flg=true;
    for(int i=m;i<n;i++)
    {
      if(nums[order[i]]-nums[order[i-m]]<=d)
      {
        flg=false;
        break;
      }
    }
    if (flg) r = m - 1;
        else l = m + 1;
  }
  cout<<l<<endl;
  vector<int>ans(n);
for(int i=0;i<n;i++)
ans[order[i]]=i%l+1;
for(auto &x:ans)cout<<x<<" ";
}

int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  int _T = 1;
  // cin >> _T;
  while (_T--)
  {
    solve();
  }
  return 0;
}

思路2

使用优先队列

维护一个递增优先队列p,其中维护每一个数列的已经放置的最后一个元素

先将元素排序后将最小的元素放到优先队列,并定义它为第一个队列的元素(ans[b[1]]=1)

每次判断从优先队列中拿出队头元素(最小),如果满足要求可以放置,那么把这个元素从队头中移除,并把这个元素放到和队头元素相同的数列中ans[b[i]]=ans[b[top]]

否则就新开一个队列,最后把这次判断的元素放到优先队列中:

1.满足要求可以放置,那么因为队头元素已经被移除,那么这个元素就代表着队头元素所在队列

2.不可以放置,那么它新开一个队列,它本身就代表了一个新的队列,也把它放到优先队列中

cpp 复制代码
// Author: zengyz
// 2025-06-07 10:59

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;

const int N = 2e5 + 10;
int a[N];
int b[N];
map<int,int>ans;
void solve()
{
  int n, m, d;
  cin >> n >> m >> d;
  priority_queue<int, vector<int>, greater<int>> p;
  for (int i = 1; i <= n; i++)
  {
    cin >> a[i];
    b[i] = a[i];
  }
  sort(b + 1, b + 1 + n);
  int cnt = 1;

  p.push(1);
  ans[b[1]] = 1;
  for (int i = 2; i <= n; i++)
  {
    auto top = p.top();
    if (b[i] - b[top] > d)
    {
      p.pop();
      ans[b[i]] = ans[b[top]];
    }
    else
    {
      ans[b[i]] = ++cnt;
    }
    p.push(i);
  }

  cout << cnt << endl;
  for (int i = 1; i <= n; i++)
  {
    cout << ans[a[i]] << " ";
  }

  return;
}

int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  int _T = 1;
  // cin >> _T;
  while (_T--)
  {
    solve();
  }
  return 0;
}

思路3

set+结构体

思路借鉴自:https://www.cnblogs.com/Chen-Jr/p/11007190.html

定义一个结构体,id表示元素的原位置,确定它的排序规则为按val排序

先将数据读取,然后使用set对结构体进行存储

使用lower_bound(其实也是二分)来查询当前集合中有没有满足要求的

定义tmp=当前元素值+间隔,即要放入当前元素所需要的最小值

不满足
s t . l o w e r _ b o u n d ( N o d e ( t m p , − 1 ) ) = = s t . e n d ( ) st.lower\_bound(Node(tmp, -1)) == st.end() st.lower_bound(Node(tmp,−1))==st.end()

即集合中没有找到,那么新开一个数组,tmp清零

否则将满足的元素取出

tmp值更新,b数组记录每个元素未排序之前的位置,并将当前元素对应的位置标记上为第几天,然后将该元素从集合中删除

其实就是在集合中不断找最小的能够满足数,满足就把当前的数接在这个元素之后,并把找到的元素删除

cpp 复制代码
// Author: zengyz
// 2025-06-07 10:59

#include <bits/stdc++.h>

using namespace std;
using i64 = long long;
struct Node
{
  int val, id;
  bool operator<(const Node &b) const
  {
    if (val == b.val)
      return id < b.id;
    else
      return val < b.val;
  }
  Node(int _val, int _id)
  {
    val = _val, id = _id;
  }
};
set<Node> st;

const int N = 2e5 + 10;
int a[N];
int b[N];
map<int, int> ans;
void solve()
{
  int n, m, d;
  cin >> n >> m >> d;
  for (int i = 1; i <= n; i++)
  {
    int num;
    cin >> num;
    st.insert(Node(num, i));
  }
  i64 tmp = 0, day = 1;
  while (!st.empty())
  {
    if (st.lower_bound(Node(tmp, -1)) == st.end())
    {
      tmp = 0;
      day++;
    }
    else
    {
      auto it = *st.lower_bound(Node(tmp, -1));
      tmp = it.val + d + 1;
      b[it.id] = day;
      st.erase(it);
    }
  }
  cout << day << endl;
  for (int i = 1; i <= n; i++)
    cout << b[i] << " ";
  return;
}

int main()
{
  ios::sync_with_stdio(0);
  cin.tie(0), cout.tie(0);
  int _T = 1;
  // cin >> _T;
  while (_T--)
  {
    solve();
  }
  return 0;
}
相关推荐
葫三生24 分钟前
如何评价《论三生原理》在科技界的地位?
人工智能·算法·机器学习·数学建模·量子计算
pipip.28 分钟前
UDP————套接字socket
linux·网络·c++·网络协议·udp
智者知已应修善业1 小时前
【51单片机用数码管显示流水灯的种类是按钮控制数码管加一和流水灯】2022-6-14
c语言·经验分享·笔记·单片机·嵌入式硬件·51单片机
拓端研究室2 小时前
视频讲解:门槛效应模型Threshold Effect分析数字金融指数与消费结构数据
前端·算法
随缘而动,随遇而安4 小时前
第八十八篇 大数据中的递归算法:从俄罗斯套娃到分布式计算的奇妙之旅
大数据·数据结构·算法
孞㐑¥5 小时前
Linux之Socket 编程 UDP
linux·服务器·c++·经验分享·笔记·网络协议·udp
IT古董5 小时前
【第二章:机器学习与神经网络概述】03.类算法理论与实践-(3)决策树分类器
神经网络·算法·机器学习
水木兰亭8 小时前
数据结构之——树及树的存储
数据结构·c++·学习·算法
Jess079 小时前
插入排序的简单介绍
数据结构·算法·排序算法
老一岁9 小时前
选择排序算法详解
数据结构·算法·排序算法