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;
}


posted @ 2025-05-31 00:51  WHUStar  阅读(29)  评论(0)    收藏  举报