【洛谷 P9421】[蓝桥杯 2023 国 B] 班级活动 题解(计数排序+贪心算法+数学)

[蓝桥杯 2023 国 B] 班级活动

题目描述

小明的老师准备组织一次班级活动。班上一共有 n n n 名( n n n 为偶数)同学,老师想把所有的同学进行分组,每两名同学一组。为了公平,老师给每名同学随机分配了一个 n n n 以内的正整数作为 id,第 i i i 名同学的 id 为 a i a_i ai

老师希望通过更改若干名同学的 id 使得对于任意一名同学 i i i,有且仅有另一名同学 j j j 的 id 与其相同( a i = a j a_i = a_j ai=aj)。请问老师最少需要更改多少名同学的 id?

输入格式

输入共 2 2 2 行。

第一行为一个正整数 n n n

第二行为 n n n 个由空格隔开的整数 a 1 , a 2 , ⋯   , a n a_1, a_2, \cdots, a_n a1,a2,,an

输出格式

输出共 1 1 1 行,一个整数。

样例 #1

样例输入 #1

4
1 2 2 3

样例输出 #1

1

提示

样例说明

仅需要把 a 1 a_1 a1 改为 3 3 3 或者把 a 4 a_4 a4 改为 1 1 1 即可。

评测用例规模与约定

  • 对于 20 % 20\% 20% 的数据,保证 n ≤ 1 0 3 n \le 10^3 n103
  • 对于 100 % 100\% 100% 的数据,保证 n ≤ 1 0 5 n \le 10^5 n105

第十四届蓝桥杯大赛软件赛决赛 C/C++ 大学 B 组 C 题


思路

首先,定义了一些常量和变量。其中,N 是数组的最大长度,n 是同学的数量,a 是存储同学ID的数组,cnt 是存储每个ID的数量的数组。xy 是两个计数器,表示需要匹配id的学生数量和需要改变id的学生数量。

读取学生的数量 n 和他们的ID a[i]。它使用一个数组 cnt[] 来计算每个ID的学生数量。

然后,它遍历 cnt[] 数组。对于每个ID i,如果有且仅有一个学生的ID为 i,则增加 x。这意味着这个学生需要匹配另一个学生的ID。如果有超过两个学生的ID相同,那么它将 y 增加 cnt[i] - 2。这意味着这些学生中的一部分需要改变他们的ID以匹配其他学生的ID。

如果一个ID只分配给了一个同学(x),那么这个同学需要找一个其他同学更改ID以形成一对。如果一个ID分配给了超过两个同学(y),那么这些同学中的一部分需要更改ID,使得每个ID只对应两个同学。在这个过程中,优先处理y,因为这些同学可以直接更改ID形成新的对,而不需要影响其他同学。

如果 x(需要匹配的学生数量)大于 y(需要改变的学生数量),那么输出 ((x - y) >> 1) + y。先让所有需要改变的学生都更改ID。剩下的部分中,每两个学生中只需要改变其中一人的ID就能匹配,所以需要除以2。

如果 x 小于或等于 y,那么输出 y,因为这些学生可以直接改变他们的ID以匹配其他学生。


AC代码

#include <algorithm>
#include <cmath>
#include <cstring>
#include <iostream>
#define AUTHOR "HEX9CF"
using namespace std;
using ll = long long;

const int N = 1e6 + 7;
const int INF = 0x3f3f3f3f;
const ll MOD = 1e9 + 7;

int n;
int a[N];
int cnt[N];

// 改别人的id
int x = 0;
// 自己要改id
int y = 0;

int main() {
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);

	memset(cnt, 0, sizeof(cnt));

	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
		cnt[a[i]]++;
	}

	for (int i = 1; i <= n; i++) {
		if (cnt[i] == 1) {
			x++;
		} else if (cnt[i] > 2) {
			y += cnt[i] - 2;
		}
	}
	if (x > y) {
		cout << ((x - y) >> 1) + y << "\n";
	} else {
		cout << y << "\n";
	}
	return 0;
}

  • 11
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值