A. Project Management
链接
https://codeforces.com/gym/105930/problem/A
思路
二分加一个简单的思维(并非简单)。 首先想到的就是排序,根据a,b的大小排序。显然注意到对同一级的a来说,b越大越好。 那么对一个group(a相同)来说,要满足后面能取到willchoose个,必然地,b_min >= willchoose。也就是这个group最小的b也要比willchoose大。·这个b_min必然是最新(a最大)的group,因为前面的group必然满足。
然后一直往后遍历,直到满足取x个or取不到x个。
代码
代码参考https://blog.csdn.net/xyl6716/article/details/148314885。我的不知道为什么一直有问题,调不出来
正确的代码:
#include <bits/stdc++.h> // 万能头
using namespace std;
// 全局变量
int n_g; // 当前测试用例的员工总数
vector<struct E> es_g; // 全局员工列表 (排序后)
vector<int> tsel_g; // chk函数使用的临时选择ID列表
vector<int> fsel_g; // 最终选择的员工ID列表
struct E {
int a, b, id; // a: 职级, b: 容忍度, id: 原始编号
// 重载 < 操作符用于排序
// 按职级 a 升序,若职级相同,则按容忍度 b 降序
bool operator<(const E& other) const {
if (a != other.a) {
return a < other.a;
}
return b > other.b; // 注意这里是 >,因为希望容忍度大的排前面
}
};
// chk函数:判断是否能选出 k_val 个人满足所有约束
bool chk(int k_val) {
if(k_val == 0) return true;
tsel_g.clear(); // 清空临时选择列表
int sc = 0; // selected count: 当前已选的总人数
int lsc = 0; // lower-rank selected count: 所有更低职级已选的人数
size_t i = 0; // 主遍历指针,指向 es_g 中当前处理的员工的起始位置
// 循环处理每个职级组,直到选够 k_val 人或遍历完所有员工
while (i < es_g.size() && sc < k_val) {
// 1. 收集当前职级的所有员工
vector<E> cre; // current rank employees: 存储当前职级的员工
size_t j = i; // 辅助指针,用于收集同职级员工
while (j < es_g.size() && es_g[j].a == es_g[i].a) {
cre.push_back(es_g[j]);
j++;
}
// 此处 cre 中的员工已按 b 降序(因为 es_g 是这样排序的)
// 2. 确定 best_t: 当前职级 cre 最多能选多少人
int best_t = 0; // 本职级最终确定选的人数
// 本职级最多还能贡献的人数,不能超过 (k_val - sc)
int mcph; // max can pick here
mcph = min((int)cre.size(), k_val - sc);
// 从“最多可选人数”往下尝试,找到第一个满足条件的 t
for (int t = mcph; t >= 1; --t) {
// 假设当前职级选 t 个人 (即 cre[0] 到 cre[t-1])
// 这 t 个人中,容忍度最低的是 cre[t-1].b
// 计算:如果当前职级贡献 t 个人,为了凑齐 k_val 人,还需要从更高职级选多少人
int h_nd = k_val - lsc - t; // higher needed
if (h_nd < 0) h_nd = 0; // 如果已经超了或够了,就不需要更高职级的人
// 判断约束:这 t 个人中 b 最小的 (cre[t-1]) 是否能容忍 h_nd 个更高职级的人
if (h_nd <= cre[t-1].b) {
best_t = t; // 记录当前职级可以选 t 个人
break; // 找到了最大的可行 t,退出循环
}
}
// 3. 将本职级选出的 best_t 个人加入选择列表,并更新计数器
for (int idx = 0; idx < best_t; ++idx) {
tsel_g.push_back(cre[idx].id);
}
sc += best_t;
lsc += best_t; // 为下一个职级组更新 lsc (之前所有职级选的人数)
i = j; // 更新主遍历指针到下一个职级组的起始位置
}
return sc == k_val; // 如果最终选的人数恰好是 k_val,则返回 true
}
// solv函数:处理单个测试用例
void solv() {
cin >> n_g; // 读取员工总数
es_g.assign(n_g, E()); // 初始化全局员工列表
vector<int> lcnt(n_g + 1, 0); //对每个等级人数进行计数, 为二分做准备
int maxcnt = 0; //记录单个等级最大人数
for (int i = 0; i < n_g; ++i) {
cin >> es_g[i].a >> es_g[i].b;
es_g[i].id = i + 1; // 记录原始编号 (1-based)
lcnt[es_g[i].a]++;
maxcnt = max(maxcnt, lcnt[es_g[i].a]);
}
sort(es_g.begin(), es_g.end()); // 排序
fsel_g.clear(); // 清空最终选择列表
// 二分查找最大的 l
int l = maxcnt - 1, r = n_g + 1;
while (l != r - 1) {
int mid = (r + l) / 2;
if (chk(mid)) {
fsel_g = tsel_g; // 更新选择方案
l = mid;
} else {
r = mid;
}
}
cout << l << "\n"; // 输出最大可选人数
for (int i = 0; i < l; ++i) { // 输出选择的员工ID
cout << fsel_g[i] << (i == l - 1 ? "" : " ");
}
cout << "\n";
}
int main() {
// 优化输入输出速度
ios_base::sync_with_stdio(false);cin.tie(NULL);
int tc; cin >> tc;//用例数量
while (tc--) {
solv(); // 处理每个测试用例
}
return 0;
}
错误的代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define tin long long
#define itn long long
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
void solve();
const int N = 2e5 + 10;
int n, t;
struct Ele
{
int a, b,id;
Ele(){}
};
bool cmp(Ele x, Ele y) { if (x.a == y.a)return x.b > y.b; return x.a < y.a; }
vector<int>ans;
Ele eles[N];
int counts[N];
//int amax = ;
bool chk(int x)
{
vector<int>nowans;
int befchoice = 0;
int idnow = 1;
bool can = true;
int nowch = 0;
for (int i = 1; i <= eles[n].a and befchoice<x; i++)
{
nowch = min(counts[i], x - befchoice);
while (nowch > 0 and eles[idnow+nowch-1].b<x-befchoice-nowch)
{
nowch--;
}
for (int j = idnow; j <= idnow + nowch - 1; j++)nowans.push_back(eles[j].id);
idnow += counts[i];
if(nowch>0)befchoice += nowch;
}
can = befchoice == x;
if (can)ans = nowans;
return can;
}
void solve()
{
cin >> n;
memset(counts, 0, sizeof(counts));
for (int i = 1; i <= n; i++)cin >> eles[i].a >> eles[i].b,eles[i].id=i;
sort(eles + 1, eles + 1 + n, cmp);
for (int i = 1; i <= n; i++)counts[eles[i].a]++;
int l = 1, r = n;
while (l < r)
{
int m = (l + r + 1) >> 1;
if (chk(m))l = m;
else r = m - 1;
}
cout << l<<'\n';
for (int i : ans)cout << i << ' ';
cout << '\n';
}
signed main()
{
IOS;
cin >> t;
while(t--)
solve();
return 0;
}
二编
不是,ai加了这段就对了?
if (l > 0) {
chk(l); // 重新生成最终方案
}
else {
ans.clear(); // k=0时清空方案
}
正确代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define tin long long
#define itn long long
#define IOS ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
void solve();
const int N = 2e5 + 10;
int n, t;
struct Ele
{
int a, b,id;
Ele(){}
};
bool cmp(Ele x, Ele y) { if (x.a == y.a)return x.b > y.b; return x.a < y.a; }
vector<int>ans;
Ele eles[N];
int counts[N];
bool chk(int x)
{
vector<int>nowans;
int befchoice = 0;
int idnow = 1; // 当前遍历位置
bool can = true;
// 重构:改为跳跃式遍历职级组
int ptr = 1; // 指针指向当前职级组起始位置
while (ptr <= n && befchoice < x) {
int cr = eles[ptr].a; // 当前职级
int cnt_here = counts[cr]; // 当前职级人数
// 计算当前职级最多可选人数
int nowch = min(cnt_here, x - befchoice);
int actual_ch = 0; // 实际选择人数
// 关键修复:从大到小尝试选择人数
for (int t = nowch; t >= 1; t--) {
// 检查约束:当前职级选t人时,最小b值是否满足条件
int higher_need = x - befchoice - t;
if (eles[ptr + t - 1].b >= higher_need) {
actual_ch = t;
break;
}
}
// 选中员工加入列表
for (int j = ptr; j < ptr + actual_ch; j++) {
nowans.push_back(eles[j].id);
}
befchoice += actual_ch;
ptr += cnt_here; // 跳到下一职级组
}
can = (befchoice == x);
if (can) ans = nowans;
return can;
}
void solve()
{
cin >> n; ans.clear();
memset(counts, 0, sizeof(counts));
for (int i = 1; i <= n; i++)cin >> eles[i].a >> eles[i].b;
for (int i = 1; i <= n;i++)eles[i].id = i;
sort(eles + 1, eles + 1 + n, cmp);
for (int i = 1; i <= n; i++)counts[eles[i].a]++;
int l = 1, r = n;
while (l < r)
{
int m = (l + r + 1) >> 1;
if (chk(m))l = m;
else r = m - 1;
}
if (l > 0) {
chk(l); // 重新生成最终方案
}
else {
ans.clear(); // k=0时清空方案
}
cout << l<<'\n';
for (int i : ans)cout << i << ' ';
cout << '\n';
}
signed main()
{
IOS;
cin >> t;
while(t--)
solve();
return 0;
}

浙公网安备 33010602011771号