P1109 学生分组

记录39

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,a[55]={},s=0,L,R,t1=0,t2=0;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		s+=a[i];
	}
	cin>>L>>R;
	double x=s*1.0/n;
	if(x<L||x>R){
		cout<<-1;
		return 0;
	}
	for(int i=1;i<=n;i++){
		if(a[i]<L) t1+=L-a[i];
		if(a[i]>R) t2+=a[i]-R;
	}
	cout<<max(t1,t2);
	return 0;
} 

题目传送门https://www.luogu.com.cn/problem/P1109


突破点

每次你可以在某组中选出一个学生把他安排到另外一组中,问最少要多少次才可以使 N 组学生的人数都在 [L,R] 中


思路

  1. 判断可不可以分
  2. 实现最小交换次数,肯定是解决低于边界或者高于边界中,二者大的那个

例如:

组数:5
每组人数:A组:8    B组:20     C组:14    D组:13   E组:15
上下边界:最少:10    最多:15

A组少了两个人,B组多了5个人

最小满足区间范围交换人数,肯定是处理掉多出来的5个人

将多出来的人给补到A组里面,然后再每组随便分多的人

无论放在哪一组,处理的都是不满足边界的人数的情况


代码简析

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,a[55]={},s=0,L,R,t1=0,t2=0;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		s+=a[i];
	}
	...
} 

a[55]={}   数组a代表每组的人数

t1=0,t2=0;       其中t1代表少的下界人数,t2代表多出来的上界限人数

s=0;      将所有组数的人加到s里面

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n,a[55]={},s=0,L,R,t1=0,t2=0;
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		s+=a[i];
	}
	cin>>L>>R;
	double x=s*1.0/n;
	if(x<L||x>R){
		cout<<-1;
		return 0;
	}
	for(int i=1;i<=n;i++){
		if(a[i]<L) t1+=L-a[i];
		if(a[i]>R) t2+=a[i]-R;
	}
	cout<<max(t1,t2);
	return 0;
} 

double x=s*1.0/n;     组数人数和平均分不一定是刚好均分的整数,所以s*1.0,并且x是double类型

if(x<L||x>R){}        x均分后不满足情况,说明无论如何都满足不了条件

if(a[i]<L) t1+=L-a[i];    存下来每组小于边界的人数
if(a[i]>R) t2+=a[i]-R;    存下来每组大于边界的人数

cout<<max(t1,t2);  二者进行比较,取其中大的人数,介绍转至思路的例子


补充

C++ 整数与浮点数类型转换完全指南


1. 核心机制:隐式类型转换规则

当整数与浮点数运算时,C++自动将整数提升为浮点类型(类型提升)。

int a = 5;
double b = a * 1.0;  // a先转为double,再乘以1.0
// 等价于:double b = static_cast<double>(a) * 1.0;

转换优先级intlong longfloatdoublelong double


2. 转换场景与精度问题

场景A:int → float

int a = 123456789;
float f = a * 1.0f;  // 或 (float)a
// 问题:float仅6-7位有效数字,可能丢失精度
// f = 123456792.0 (后几位不准确)

结论:当 int > 2^24 (约1670万) 时,转float可能精度丢失。

场景B:int → double

int a = 123456789;
double d = a * 1.0;  // 推荐
// double有15-16位有效数字,完全保留int范围(-2^31~2^31-1)

结论intdouble安全,无精度损失。

场景C:long long → double

long long ll = 1e18;
double d = ll * 1.0;
// 问题:double约15位有效数字,1e18有19位,可能丢失低位

结论:当 long long > 2^53 (约9e15) 时,转double会精度丢失。


3. 整数除法陷阱与修复

错误:整数除法

int a = 5, b = 2;
double c = a / b;  // 结果:c = 2.0(不是2.5!)
// 原因:先进行整数除法得到2,再转为double
正确:转换为浮点再除

// 方法1:乘1.0(竞赛最常用)
double c1 = a * 1.0 / b;  // 5.0/2 = 2.5

// 方法2:显式转换
double c2 = double(a) / b;
double c3 = (double)a / b;

// 方法3:使用static_cast
double c4 = static_cast<double>(a) / b;

4. 类型转换大小等级

// 从小到大自动转换(安全)
char → short → int → long long → float → double → long double

// 从大向小需要强制转换(可能溢出/丢失精度)
double → int  // 截断小数,非四舍五入
截断示例

double d = 3.99;
int a = d;  // a = 3(直接截断,不是4!)
// 要四舍五入需用:int a = d + 0.5;

5. 竞赛高频使用场景

场景1:计算平均值

int sum = 15, cnt = 4;
double avg = sum * 1.0 / cnt;  // 3.75 避免整数除法
场景2:防止中间乘积溢出

int a = 1e9, b = 1e9;
long long c = a * 1LL * b;  // 先转long long再乘,避免int溢出
// 错误:long long c = a * b;  // 先int溢出,再转long long
场景3:比较浮点数

double a = 0.1 * 3;
double b = 0.3;
// 错误:if (a == b)  // 可能因精度问题不相等

// 正确:使用eps
const double EPS = 1e-9;
if (fabs(a - b) < EPS) {  // 认为相等
    // ...
}

6. 性能与开销

转换类型性能影响原因
int → double极小CPU硬件原生支持,单指令完成
long long → double较小可能需要多周期,但可忽略
double → int中等需要截断指令,约3-5个时钟周期
多次转换累计在循环中应减少重复转换

竞赛建议:无需过度优化类型转换,正确性优先


7. 显式转换 vs 隐式转换

int a = 5;
double b;

// 隐式转换(不推荐,可能隐藏警告)
b = a;  // 自动转换,可能产生编译警告

// 显式转换(推荐,代码意图明确)
b = static_cast<double>(a);  // C++风格,最规范
b = (double)a;               // C风格,兼容性好
b = a * 1.0;                 // 竞赛常用,简洁

8. 常见错误与调试技巧

错误1:忘记转换导致整数除法

// 错误代码
int a = 5, b = 2;
cout << a / b;  // 输出2

// 修复
cout << a * 1.0 / b;  // 输出2.5
错误2:转换时机错误

int a = 5, b = 2;
double c = (double)(a / b);  // 错误:先整数除法得2,再转2.0
double d = (double)a / b;    // 正确:a先转5.0,再除得2.5
错误3:精度丢失未察觉

int n = 2000000000;  // 2e9
float f = n;         // 精度丢失,可能变成1999999872
cout << f;           // 输出错误结果
// 解决:用double

9. CSP竞赛最佳实践

// 模板1:安全除法
inline double safe_div(int a, int b) {
    return a * 1.0 / b;
}

// 模板2:防溢出乘法
inline long long safe_mul(int a, int b) {
    return a * 1LL * b;
}

// 模板3:浮点数比较
inline bool double_equal(double a, double b) {
    const double EPS = 1e-9;
    return fabs(a - b) < EPS;
}

// 主代码
int main() {
    int a, b;
    cin >> a >> b;
    
    // ✅ 正确姿势
    double avg = a * 1.0 / b;
    long long product = a * 1LL * b;
    
    // ✅ 四舍五入
    int rounded = avg + 0.5;
    
    return 0;
}

10. 核心要点速查表

需求推荐写法结果/注意事项
整数→浮点除法a * 1.0 / b得到精确小数结果
int→doubleint * 1.0安全,无精度损失
int→floatint * 1.0f大数可能精度丢失
防溢出a * 1LL * ba和b先转long long
四舍五入int(d + 0.5)不是强制转换
double→int(int)d截断,非四舍五入
浮点相等判断fabs(a-b) < EPS避免直接用 ==

终极建议:在CSP中,记住 *1.0*1LL 两个技巧,可解决90%的类型转换问题。精度问题一律用double,避免使用float

### C语言实现学生分组算法 为了实现学生分组功能,可以采用多种方法。这里提供一种基于贪心算法的方法来分配学生到不同的小组中,使得各小组人数尽可能均衡。 #### 定义结构体表示学生和班级 首先定义必要的数据结构: ```c #include <stdio.h> #include <stdlib.h> typedef struct { int id; } Student; typedef struct { int group_id; int student_count; Student* students[10]; // 假设每组最多有10名成员 } Group; ``` #### 初始化函数 初始化一定数量的学生对象以及若干个空闲的小组容器: ```c void init_students(Student students[], int count) { for(int i = 0; i < count; ++i){ students[i].id = i + 1; } } void init_groups(Group groups[], int num_of_groups) { for (int i = 0; i < num_of_groups; ++i) { groups[i].group_id = i + 1; groups[i].student_count = 0; } } ``` #### 贪心策略分配学生至各个小组 遍历所有待分配的学生列表,并将其加入当前最少学生的那一个小组里去: ```c void assign_to_group(Group groups[], const int num_of_groups, Student s) { int min_index = 0; for (int j = 1; j < num_of_groups; ++j) { if(groups[j].student_count < groups[min_index].student_count){ min_index = j; } } groups[min_index].students[(groups[min_index].student_count)++] = &s; } ``` #### 主程序部分 设置好输入参数之后就可以执行具体的分组操作了: ```c #define NUM_OF_STUDENTS 25 #define NUM_OF_GROUPS 5 int main(){ Student all_students[NUM_OF_STUDENTS]; Group available_groups[NUM_OF_GROUPS]; init_students(all_students, NUM_OF_STUDENTS); init_groups(available_groups, NUM_OF_GROUPS); for (int k = 0; k < NUM_OF_STUDENTS; ++k) { assign_to_group(available_groups, NUM_OF_GROUPS, all_students[k]); } // 打印结果... for (int l = 0; l < NUM_OF_GROUPS; ++l) { printf("Group %d:\n", available_groups[l].group_id); for (int m = 0; m < available_groups[l].student_count; ++m) { printf("\tStudent ID:%d\n", available_groups[l].students[m]->id); } } return 0; } ``` 此段代码展示了如何通过简单的循环与条件判断完成基本的学生分组任务[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值