题解 | #奥赛组队#

奥赛组队

https://www.nowcoder.com/practice/8809c78fe4fa445ab8218cee5b69910f

题目链接

题目描述

某校共有 名学生,第 名学生拥有两项能力:编程能力 与体育能力 。即将到来的奥林匹克竞赛包括编程赛道与体育赛道,学校计划组建两支参赛队伍:编程队伍人数恰为 ;体育队伍人数恰为 ;同一名学生不能同时进入两支队伍。学校的总实力定义为两支队伍实力之和:编程队伍实力为其所有队员编程能力之和;体育队伍实力为其所有队员体育能力之和。请你为学校选择队员,使得总实力最大,并输出一个可行方案。

输入:

  • 一行三个正整数:,分别表示学生总数、编程队伍人数和体育队伍人数。
  • 一行 个整数:,表示第 名学生的编程能力。
  • 一行 个整数:,表示第 名学生的体育能力。

输出:

  • 第一行输出一个整数,表示最大总实力。
  • 第二行输出 个互不相同的整数,表示编程队伍成员的编号(按输入顺序,从 )。
  • 第三行输出 个互不相同的整数,表示体育队伍成员的编号。若存在多种最优方案,可输出任意一种。

解题思路

我们使用“贪心+优先队列”策略来组建两支队伍:

  1. 首先按编程能力 降序排序,选出前 名学生加入编程队(标记 op[id]=1),并累加其编程实力到 ans
  2. 排序后按原编号恢复顺序;构建三种优先队列:
    • q1:未入编程队学生的小顶堆,按编程能力 排序(greater<node>)。
    • q2:未入体育队学生的小顶堆,按体育能力 排序。
    • q3:已入编程队学生的小顶堆,按边际收益 排序。
  3. 重复 次选择体育队员:
    • q2 中获取当前最大 的未选学生,增益为 (方案1)。
    • q3 中获取当前最大边际收益的在编程队员 ,并从 q1 中获取最大 的未选学生 ,交换二者: 转入体育队、 转入编程队,增益为 (方案2)。
    • 比较两种增益,取更大者更新 ans,并维护 op 数组及各堆状态。
  4. 最终 ans 即最大总实力,op[id]==1 的为编程队,op[id]==2 的为体育队。

代码

#include <iostream>
#include <queue>
#include <algorithm>
using namespace std;
const int N = 3005;
struct node {
    int id, num;
    node(int x = 0, int y = 0) : id(x), num(y) {}
    bool operator<(const node &p) const { return num > p.num; }
    bool operator>(const node &p) const { return num < p.num; }
} a[N], b[N];
bool cmpId(const node &x, const node &y) { return x.id < y.id; }
priority_queue<node, vector<node>, greater<node>> q1, q2, q3;
int op[N];
int main() {
    int n, X, Y;
    cin >> n >> X >> Y;
    for (int i = 1; i <= n; i++) { cin >> a[i].num; a[i].id = i; }
    for (int i = 1; i <= n; i++) { cin >> b[i].num; b[i].id = i; }
    // 按编程能力降序选前 X 名加入编程队
    sort(a + 1, a + n + 1);
    long long ans = 0;
    for (int i = 1; i <= X; i++) {
        op[a[i].id] = 1;
        ans += a[i].num;
    }
    // 恢复原顺序
    sort(a + 1, a + n + 1, cmpId);
    // 初始化优先队列
    for (int i = 1; i <= n; i++) {
        q1.push(node(i, a[i].num));        // 编程能力队列
        q2.push(node(i, b[i].num));        // 体育能力队列
        if (op[i] == 1) {
            q3.push(node(i, b[i].num - a[i].num)); // 边际收益队列
        }
    }
    // 选 Y 名体育队成员
    for (int cnt = 0; cnt < Y; cnt++) {
        long long best = LLONG_MIN;
        int id1 = -1, id2 = -1, opt = 0;
        // 方案1:直接选取最高体育能力
        while (!q2.empty() && op[q2.top().id] != 0) q2.pop();
        if (!q2.empty()) {
            best = q2.top().num;
            id1 = q2.top().id;
            opt = 1;
        }
        // 方案2:交换编程队与未选中队员
        while (!q3.empty() && op[q3.top().id] != 1) q3.pop();
        while (!q1.empty() && op[q1.top().id] != 0) q1.pop();
        if (!q3.empty() && !q1.empty()) {
            long long gain = q3.top().num + q1.top().num;
            if (gain > best) {
                best = gain;
                id1 = q3.top().id;
                id2 = q1.top().id;
                opt = 2;
            }
        }
        ans += best;
        if (opt == 1) {
            op[id1] = 2;
        } else {
            op[id1] = 2;
            op[id2] = 1;
            q1.pop();
            q3.pop();
            q3.push(node(id2, b[id2].num - a[id2].num));
        }
    }
    // 输出结果
    cout << ans << '\n';
    for (int i = 1; i <= n; i++) if (op[i] == 1) cout << i << ' ';
    cout << '\n';
    for (int i = 1; i <= n; i++) if (op[i] == 2) cout << i << ' ';
    cout << '\n';
    return 0;
}

import java.util.*;
public class Main {
    static class Node {
        int id; long num;
        Node(int id, long num) { this.id = id; this.num = num; }
    }
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        int n = sc.nextInt(), X = sc.nextInt(), Y = sc.nextInt();
        Node[] a = new Node[n + 1];
        Node[] b = new Node[n + 1];
        for (int i = 1; i <= n; i++) a[i] = new Node(i, sc.nextLong());
        for (int i = 1; i <= n; i++) b[i] = new Node(i, sc.nextLong());
        Arrays.sort(a, 1, n + 1, (x, y) -> Long.compare(y.num, x.num));
        long ans = 0;
        int[] op = new int[n + 1]; // 0: 未选, 1: 编程队, 2: 体育队
        for (int i = 1; i <= X; i++) { op[a[i].id] = 1; ans += a[i].num; }
        Arrays.sort(a, 1, n + 1, (x, y) -> Integer.compare(x.id, y.id));
        PriorityQueue<Node> q1 = new PriorityQueue<>((x, y) -> Long.compare(y.num, x.num)); // a_i 最大堆(未选)
        PriorityQueue<Node> q2 = new PriorityQueue<>((x, y) -> Long.compare(y.num, x.num)); // b_i 最大堆(未选)
        PriorityQueue<Node> q3 = new PriorityQueue<>((x, y) -> Long.compare(y.num, x.num)); // (b_i-a_i) 最大堆(已在编程)
        for (int i = 1; i <= n; i++) {
            q1.add(new Node(i, a[i].num));
            q2.add(new Node(i, b[i].num));
            if (op[i] == 1) q3.add(new Node(i, b[i].num - a[i].num));
        }
        for (int cnt = 0; cnt < Y; cnt++) {
            long best = Long.MIN_VALUE; int id1 = -1, id2 = -1; int opt = 0;
            while (!q2.isEmpty() && op[q2.peek().id] != 0) q2.poll();
            if (!q2.isEmpty()) { best = q2.peek().num; id1 = q2.peek().id; opt = 1; }
            while (!q1.isEmpty() && op[q1.peek().id] != 0) q1.poll();
            while (!q3.isEmpty() && op[q3.peek().id] != 1) q3.poll();
            if (!q1.isEmpty() && !q3.isEmpty()) {
                long gain = q1.peek().num + q3.peek().num; // a_j + (b_i - a_i)
                if (gain > best) { best = gain; id1 = q3.peek().id; id2 = q1.peek().id; opt = 2; }
            }
            ans += best;
            if (opt == 1) {
                q2.poll();
                op[id1] = 2;
            } else {
                q1.poll(); q3.poll();
                op[id1] = 2; op[id2] = 1;
                q3.add(new Node(id2, b[id2].num - a[id2].num));
            }
        }
        System.out.println(ans);
        StringBuilder sb = new StringBuilder();
        for (int i = 1; i <= n; i++) if (op[i] == 1) sb.append(i).append(' ');
        System.out.println(sb.toString().trim());
        sb.setLength(0);
        for (int i = 1; i <= n; i++) if (op[i] == 2) sb.append(i).append(' ');
        System.out.println(sb.toString().trim());
    }
}

def main():
    import sys
    input = sys.stdin.readline
    n,X,Y = map(int, input().split())
    a = [0] + list(map(int, input().split()))
    b = [0] + list(map(int, input().split()))
    NEG_INF = -10**18
    dp = [[[NEG_INF]*(Y+1) for _ in range(X+1)] for __ in range(n+1)]
    choice = [[[0]*(Y+1) for _ in range(X+1)] for __ in range(n+1)]
    dp[0][0][0] = 0
    for i in range(1, n+1):
        for j in range(X+1):
            for k in range(Y+1):
                dp[i][j][k] = dp[i-1][j][k]
                choice[i][j][k] = 0
                if j>0 and dp[i-1][j-1][k] + a[i] > dp[i][j][k]:
                    dp[i][j][k] = dp[i-1][j-1][k] + a[i]
                    choice[i][j][k] = 1
                if k>0 and dp[i-1][j][k-1] + b[i] > dp[i][j][k]:
                    dp[i][j][k] = dp[i-1][j][k-1] + b[i]
                    choice[i][j][k] = 2
    ans = dp[n][X][Y]
    print(ans)
    P, S = [], []
    i, j, k = n, X, Y
    while i>0:
        if choice[i][j][k] == 1:
            P.append(i)
            j -= 1
        elif choice[i][j][k] == 2:
            S.append(i)
            k -= 1
        i -= 1
    print(*P[::-1])
    print(*S[::-1])

if __name__ == '__main__':
    main()

算法及复杂度

  • 算法:贪心 + 优先队列。
  • 时间复杂度:
  • 空间复杂度:
全部评论
考虑南京OD的宝子们看过来,你就是我们要找的人,一对一指导,可私信
点赞 回复 分享
发布于 2025-08-09 16:48 贵州

相关推荐

2025-12-25 10:16
已编辑
合肥工业大学 后端工程师
如题,在历经了长达多月的焦急等待,楼主也算是如愿以偿收到了梦中情司的意向了,秋招也终于是落下了帷幕,虽然手中的offer不够打牌,但已经满足了。华为时间线:9.3&nbsp;笔试环节,惊险通过10.15&nbsp;线下面试,前两轮技术面手撕都比较轻松,面试官态度也很好,最后一轮主管面,向主管表达了强烈的意愿,主管很和蔼,面试体验非常棒,1125定律后入池成功11.19&nbsp;收到接口人的保温电话12.9&nbsp;接到部门hr的保温电话,介绍了一下部门负责的工作12.23&nbsp;收到华为的意向书,成为华孝子一枚~期间收到了之前实习过的公司的offer,害怕华子泡不出来就先签三方了,这下不得不毁约了,在此向前司道个歉,也感谢前司对我的认可和托举,祝业务蒸蒸日上~感谢从今年三月开始找暑期实习以来,所有朋友和家人的鼓励,我们宿舍的就业氛围相当好,大家会分享各种有用的信息以及面试中遇到刁钻的面试题,在有人收到offer的时候我们都会发自内心的高兴和祝福,在我去线下面的时候也借我穿过西服.....能在大学四年分入这么好的宿舍拥有这么这么好的舍友除了幸运我找不出其他的形容词。还要感谢我的父母,在我每一次面试前都给予鼓励,也在失败的时候安慰我,他们的托底是我前进的基石,以后有工资了要给父母买很多东西最感谢的是我的女朋友,我们从大一相识,一直坚持到大四,她是一个非常优秀也异常坚定的女生,也正是因为她的实力出众早再年初就定好了要去上海的一家外企。我为了也去上海,从暑期实习开始投了不少上海的岗位但无一例外的都被拒之门外,但这期间她从来没有嫌弃过我,反而一直鼓励我相信我,如果说父母的托底是我前进的基石,那女朋友的鼓励和信任则是我前进的动力和方向。在如今这个充满戾气和对立的社会,能找到一个一心一意彼此喜欢的人实在是很难得,我深知这种珍贵所以更会加倍珍惜也感谢自己吧,在经历了无数个失眠的夜晚和面试失败的打击下,最终还是迎来了最好的结果,记得在华为线下面的前几周我几乎回到了高三时期的作息,那真是一段充实美好的时光,好在最后的结果也没有辜负这份努力也想跟所有的牛友说:不要因为一时的失败而自怨自艾,妄自菲薄,只要坚持下去,总会有柳暗花明又一村的惊喜在等待着你,机会总是垂青于有准备的人,要相信否极泰来,相信自己。朋友,坚定地相信未来吧,相信不屈不挠的努力,相信战胜死亡的年轻,相信未来、热爱生命。
小肥罗:有这样的女朋友真是幸福
秋招白月光
点赞 评论 收藏
分享
评论
1
收藏
分享

创作者周榜

更多
牛客网
牛客网在线编程
牛客网题解
牛客企业服务