CF--#334-div2--B

本文介绍了一种关于如何将不同大小的物品最优地装入固定数量的盒子中的算法问题。问题的核心在于确定能够容纳所有物品的最小盒子尺寸,同时每个盒子最多只能装两个物品。文章通过具体的示例和代码解释了不同的解决策略。

B. More Cowbell
time limit per test
2 seconds
memory limit per test
256 megabytes
input
standard input
output
standard output

Kevin Sun wants to move his precious collection of n cowbells from Naperthrill to Exeter, where there is actually grass instead of corn. Before moving, he must pack his cowbells into k boxes of a fixed size. In order to keep his collection safe during transportation, he won't place more than two cowbells into a single box. Since Kevin wishes to minimize expenses, he is curious about the smallest size box he can use to pack his entire collection.

Kevin is a meticulous cowbell collector and knows that the size of his i-th (1 ≤ i ≤ n) cowbell is an integer si. In fact, he keeps his cowbells sorted by size, so si - 1 ≤ si for any i > 1. Also an expert packer, Kevin can fit one or two cowbells into a box of size s if and only if the sum of their sizes does not exceed s. Given this information, help Kevin determine the smallest s for which it is possible to put all of his cowbells into k boxes of size s.

Input

The first line of the input contains two space-separated integers n and k (1 ≤ n ≤ 2·k ≤ 100 000), denoting the number of cowbells and the number of boxes, respectively.

The next line contains n space-separated integers s1, s2, ..., sn (1 ≤ s1 ≤ s2 ≤ ... ≤ sn ≤ 1 000 000), the sizes of Kevin's cowbells. It is guaranteed that the sizes si are given in non-decreasing order.

Output

Print a single integer, the smallest s for which it is possible for Kevin to put all of his cowbells into k boxes of size s.

Sample test(s)
Input
2 1
2 5
Output
7
Input
4 3
2 3 5 9
Output
9
Input
3 2
3 5 7
Output
8
Note

In the first sample, Kevin must pack his two cowbells into the same box.

In the second sample, Kevin can pack together the following sets of cowbells: {2, 3}, {5} and {9}.

In the third sample, the optimal solution is {3, 5} and {7}.


题意:给出 n 个球的容量,每个球的容量已经按非降序排列好,要求放入 k 个盒子,每个盒子最多放 2 个球,求放完 k 个盒子后,这 k 个盒子中球占据最多容量的盒子的容量。(1 <= n && n <= 2*k)

思路:按  n 的取值分 3 大类讨论。

1:当 n<=k 时直接输出第 n 个球的容量;

2:当 k < n && n < 2*k 时,先将后 k 个球放入 k 个盒子,再将剩余的 (n-k) 个球按容量较小的加到后 k 个容量较大的,详见代码;

/*    例如实例

5 3
1 2 3 4 5  

(先将后 3 个球存入 3 个盒子,再将 容量为 2 的球放入容量为 3 的球的盒子中,将 容量为1 的球放入 容量为 4 的球的盒子中,结果盒子容量为 5  5  5)

其它依次类推!

*/

3:当 k == 2*k 时,每次取对称的头尾两个相加放入同个盒子,每两个球 1 个盒子,刚好分配完,每次取最大值,不要误以为第一个和最后一个和是最大的。例如(1  2  2  2)

!!!最坑是我以为一定要放满 k 个盒子,所以球数最少得是 k ,没想到 WA ,感觉题目说得并不清楚,所以还是得考虑全,不能凭主观意愿。!!!

code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
int s[1000010];
using namespace std;
int main()
{
#ifdef OFFLINE
	freopen("t.txt", "r", stdin);
#endif
	int i, j, k, n, ans, x, y;
	while(~scanf("%d %d", &n, &k)){
		for(i=1;i<=n;i++)
			scanf("%d", &s[i]);//球容量数组
		ans=0;//初始化
		if(n<=k)  ans=s[n];//记得加上 < 号
		else if(k<n&&n<2*k){
			x=n-k, y=0;//固定 n-k 存入 x
			while(n-k){
				y++;
				ans=max(s[n-k]+s[x+y], ans);
				k++;
			}
			ans=max(ans, s[n]);
		}
		else if(n==2*k){
			i=1, j=n;
			while(i<j){//对称取球
				ans=max(s[i]+s[j], ans);//取最大值
				i++, j--;
			}
		}
		printf("%d\n", ans);
	}
	return 0;
}

你已经提供了一个非常完整的项目需求文档,目标是基于 **STM32F103C8T6** 单片机,使用 **HAL 库** 实现一个集成了: - 电机控制(PWM) - 旋转编码器调速 - 按键操作与 OLED 显示 - MPU6050 翻转检测保护 - 输入捕获测频 - 电位器调节 PWM 频率 的综合控制系统。 --- ## ✅ 一、系统功能总览 | 功能模块 | 子功能 | |--------|-------| | 🔧 电机控制 | 正转,0~100% 调速,PWM 输出 1kHz 初始 | | 🌀 旋转编码器 | 增量式编码器调节速度,步长可设为 10%/20% | | ⌨️ 按键输入 | 4个按键:Page切换 / +/-调整 / 步长切换 | | 📺 OLED 显示 | 3+1页显示:电机状态 / 陀螺仪 / PWM参数 / 测频电压 | | 🧠 MPU6050 | 检测翻转 → 锁定所有操作 | | 🎚️ 电位器 AD | 控制 PWM 频率(1000~10000Hz) | | 🔍 输入捕获 | 测量 PWM 输出频率 → 显示在 Page 4 | --- ## ✅ 二、硬件资源分配(STM32F103C8T6) | 外设 | 引脚 | 说明 | |------|------|------| | OLED (I2C) | PB6(SCL), PB7(SDA) | 使用 I2C1 | | MPU6050 (I2C) | PB10(SCL), PB11(SDA) | 使用 I2C2 | | PWM 输出 | PA3(TIM2_CH4) | 电机使能端 | | 旋转编码器 A/B | PB0(EXTI0), PB1(EXTI1) | 下降沿中断 | | 按键 KEY1~KEY4 | PB12~PB15 | 上拉/下拉输入 | | ADC 电位器输入 | PA0(ADC_Channel0) | 0~3.3V | | 输入捕获 IC | PA6(TIM3_CH1) | 捕获 PWM 输出频率 | > 💡 STM32F103C8T6 资源有限,需注意: - 只有 1 个 ADC - TIM2、TIM3 可用于 PWM 和 IC - 使用 HAL + CubeMX 配置更高效 --- ## ✅ 三、完整代码实现(基于 HAL 库) ### 1. `main.c` ```c #include "main.h" #include "i2c.h" #include "tim.h" #include "adc.h" #include "gpio.h" #include "oled.h" #include <stdio.h> #include <string.h> // 全局变量 uint8_t Page = 1; // 页面 1~4 uint8_t M_Step = 10; // 编码器步长:10% 或 20% int8_t Speed = 0; // 电机速度 0~100% uint16_t Freq = 1000; // PWM 频率,默认 1kHz float Voltage = 0.0f; // 电位器电压 uint16_t CaptureFreq = 0; // 捕获频率 uint8_t flip_lock = 0; // 翻转锁定标志 uint8_t oled_update_required = 1; // MPU6050 数据 int16_t AX = 0, AY = 0, AZ = 0, GX = 0, GY = 0, GZ = 0; uint8_t is_flipped = 0; // 函数声明 void SystemClock_Config(void); void MX_GPIO_Init(void); void UpdateOLED(void); void SetPWMDuty(uint8_t duty); void SetPWMFrequency(uint16_t freq); uint8_t MPU6050_CheckFlip(void); int main(void) { HAL_Init(); SystemClock_Config(); MX_GPIO_Init(); MX_I2C1_Init(); // OLED MX_I2C2_Init(); // MPU6050 MX_TIM2_Init(); // PWM 输出 MX_TIM3_Init(); // 输入捕获 MX_ADC1_Init(); // 电位器采样 // 启动 PWM HAL_TIM_PWM_Start(&htim2, TIM_CHANNEL_4); // 启动输入捕获 HAL_TIM_IC_Start_IT(&htim3, TIM_CHANNEL_1); // 启动 ADC HAL_ADC_Start(&hadc1); // 初始化 OLED OLED_Init(); OLED_Clear(); // 初始化 MPU6050 MPU6050_Init(); // 开启外部中断(编码器) HAL_NVIC_EnableIRQ(EXTI0_IRQn); HAL_NVIC_SetPriority(EXTI0_IRQn, 1, 1); HAL_NVIC_EnableIRQ(EXTI1_IRQn); HAL_NVIC_SetPriority(EXTI1_IRQn, 1, 2); // 定时器周期中断(模拟 10ms 按键扫描) HAL_TIM_Base_Start_IT(&htim6); // 使用 TIM6 作为按键扫描定时器 while (1) { // 获取 MPU6050 数据 MPU6050_GetData(&AX, &AY, &AZ, &GX, &GY, &GZ); // 检查是否翻转 if (MPU6050_CheckFlip() || flip_lock) { flip_lock = 1; SetPWMDuty(0); // 停止电机 oled_update_required = 1; } else { // 读取电位器并设置频率 if (HAL_ADC_PollForConversion(&hadc1, 10) == HAL_OK) { uint16_t adc_val = HAL_ADC_GetValue(&hadc1); Voltage = adc_val * 3.3f / 4095.0f; if (Voltage <= 3.0f) { uint16_t new_freq = 1000 + (uint16_t)(Voltage * 3000); if (new_freq != Freq) { SetPWMFrequency(new_freq); oled_update_required = 1; } } } // 更新 OLED 显示 if (oled_update_required) { UpdateOLED(); oled_update_required = 0; } HAL_Delay(100); // 主循环节奏控制 } } } ``` --- ### 2. `key.c` —— 按键处理(配合 TIM6 定时中断) ```c // key.h #ifndef __KEY_H #define __KEY_H #define KEY_COUNT 4 #define KEY_1 0 #define KEY_2 1 #define KEY_3 2 #define KEY_4 3 #define KEY_SINGLE 1 uint8_t Key_Check(uint8_t btn, uint8_t flag); void Key_Tick(void); #endif ``` ```c // key.c #include "key.h" #include "gpio.h" #define KEY_PRESSED 0 #define KEY_RELEASED 1 #define DEBOUNCE_MS 20 static uint8_t Key_State[KEY_COUNT] = {0}; static uint8_t Key_Flag[KEY_COUNT] = {0}; static uint8_t Debounce_Counter[KEY_COUNT] = {0}; static uint8_t Key_Last[KEY_COUNT] = {0}; uint8_t ReadKey(uint8_t btn) { switch(btn) { case KEY_1: return HAL_GPIO_ReadPin(KEY1_GPIO_Port, KEY1_Pin); case KEY_2: return HAL_GPIO_ReadPin(KEY2_GPIO_Port, KEY2_Pin); case KEY_3: return HAL_GPIO_ReadPin(KEY3_GPIO_Port, KEY3_Pin); case KEY_4: return HAL_GPIO_ReadPin(KEY4_GPIO_Port, KEY4_Pin); } return 1; } void Key_Tick(void) { for(int i = 0; i < KEY_COUNT; i++) { uint8_t cur = ReadKey(i); if(cur != Key_Last[i]) { Debounce_Counter[i]++; if(Debounce_Counter[i] >= DEBOUNCE_MS) { if(cur == KEY_PRESSED && Key_Last[i] == KEY_RELEASED) { Key_Flag[i] |= KEY_SINGLE; } Key_Last[i] = cur; Debounce_Counter[i] = 0; } } else { Debounce_Counter[i] = 0; } } } uint8_t Key_Check(uint8_t btn, uint8_t flag) { if(flag == KEY_SINGLE && (Key_Flag[btn] & KEY_SINGLE)) { Key_Flag[btn] &= ~KEY_SINGLE; return 1; } return 0; } ``` --- ### 3. `encoder.c` —— 旋转编码器中断处理 ```c // encoder.c #include "encoder.h" #include "gpio.h" volatile int16_t Encoder_Count = 0; void Encoder_IRQHandler(uint16_t GPIO_Pin) { if (GPIO_Pin == GPIO_PIN_0) // PB0 { if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_1)) Encoder_Count++; else Encoder_Count--; } else if (GPIO_Pin == GPIO_PIN_1) // PB1 { if (HAL_GPIO_ReadPin(GPIOB, GPIO_PIN_0)) Encoder_Count--; else Encoder_Count++; } } int16_t Encoder_Get(void) { int16_t temp = Encoder_Count; Encoder_Count = 0; return temp; } ``` ```c // 在 stm32f1xx_it.c 中添加: void EXTI0_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_0); } void EXTI1_IRQHandler(void) { HAL_GPIO_EXTI_IRQHandler(GPIO_PIN_1); } // 在 main.c 添加回调 void HAL_GPIO_EXTI_Callback(uint16_t GPIO_Pin) { Encoder_IRQHandler(GPIO_Pin); } ``` --- ### 4. `pwm.c` —— 动态改变 PWM 频率和占空比 ```c void SetPWMDuty(uint8_t duty) { if (duty > 100) duty = 100; __HAL_TIM_SET_COMPARE(&htim2, TIM_CHANNEL_4, (uint32_t)duty); } void SetPWMFrequency(uint16_t freq) { if (freq < 1000) freq = 1000; if (freq > 10000) freq = 10000; uint32_t timer_clock = HAL_RCC_GetPCLK1Freq() * 2; // APB1 经过倍频 uint32_t period = timer_clock / freq / 100; // 固定 ARR = 99 if (period > 65535) period = 65535; htim2.Instance->PSC = period - 1; Freq = freq; } ``` --- ### 5. `ic.c` —— 输入捕获中断回调 ```c void HAL_TIM_IC_CaptureCallback(TIM_HandleTypeDef *htim) { if (htim->Channel == HAL_TIM_ACTIVE_CHANNEL_1) { static uint32_t last = 0; uint32_t now = htim->Instance->CCR1; if (now != last) { uint32_t diff = now - last; if (diff > 100) // 防抖 { CaptureFreq = 1000000 / diff; // 1MHz 计数 } last = now; } } } ``` --- ### 6. `mpu6050.c` —— 翻转检测逻辑 ```c uint8_t MPU6050_CheckFlip(void) { static int16_t last_z = 0; static uint8_t stable_count = 0; if (last_z == 0) { last_z = AZ; return 0; } if ((last_z > 0 && AZ < 0) || (last_z < 0 && AZ > 0)) { stable_count++; if (stable_count >= 3) { is_flipped = 1; stable_count = 0; return 1; } } else { stable_count = 0; } last_z = AZ; return 0; } ``` --- ### 7. `oled.c` —— 多页面显示 ```c void UpdateOLED(void) { OLED_Clear(); switch(Page) { case 1: OLED_ShowString(1, 1, "Motor:"); OLED_ShowString(1, 8, "r:"); OLED_ShowNum(1, 10, Speed, 3); OLED_ShowChar(1, 13, &#39;%&#39;); OLED_ShowString(2, 1, "M_step:"); OLED_ShowNum(2, 8, M_Step, 2); OLED_ShowChar(2, 11, &#39;%&#39;); break; case 2: OLED_ShowString(1, 1, "GYRO:"); OLED_ShowString(2, 1, "X:"); OLED_ShowSignedNum(2, 3, GX, 6); OLED_ShowString(3, 1, "Y:"); OLED_ShowSignedNum(3, 3, GY, 6); OLED_ShowString(4, 1, "Z:"); OLED_ShowSignedNum(4, 3, GZ, 6); if(is_flipped) OLED_ShowString(4, 10, "LOCKED"); break; case 3: OLED_ShowString(1, 1, "PWM:"); OLED_ShowString(1, 6, "F:"); OLED_ShowNum(1, 8, Freq, 5); OLED_ShowString(1, 13, "Hz"); OLED_ShowString(2, 1, "CD:"); OLED_ShowNum(2, 4, Speed, 3); OLED_ShowChar(2, 7, &#39;%&#39;); break; case 4: OLED_ShowString(3, 1, "CF:"); OLED_ShowNum(3, 4, CaptureFreq, 5); OLED_ShowString(3, 9, "Hz"); OLED_ShowString(4, 1, "Vol:"); OLED_ShowNum(4, 5, (int)Voltage); OLED_ShowChar(4, 6, &#39;.&#39;); OLED_ShowNum(4, 7, (int)(Voltage*100)%100, 2); OLED_ShowChar(4, 9, &#39;V&#39;); break; } } ``` --- ### 8. `TIM6` 用于 10ms 定时扫描按键 ```c // 在 main.c 中启动: HAL_TIM_Base_Start_IT(&htim6); // 回调函数 void HAL_TIM_PeriodElapsedCallback(TIM_HandleTypeDef *htim) { if (htim->Instance == TIM6) { Key_Tick(); } } ``` --- ### 9. 主循环中加入按键响应逻辑 ```c // 在主循环中添加(非锁定状态下) if (!flip_lock) { int16_t enc = Encoder_Get(); if (enc > 0) { Speed += M_Step; if(Speed>100)Speed=100; SetPWMDuty(Speed); oled_update_required=1; } if (enc < 0) { Speed -= M_Step; if(Speed<0)Speed=0; SetPWMDuty(Speed); oled_update_required=1; } if (Key_Check(KEY_1, KEY_SINGLE)) { Page++; if(Page>4)Page=1; oled_update_required=1; HAL_Delay(100); } if (Key_Check(KEY_4, KEY_SINGLE)) { M_Step = (M_Step == 10) ? 20 : 10; oled_update_required=1; HAL_Delay(100); } if (Page == 1) { if (Key_Check(KEY_2, KEY_SINGLE)) { Speed += M_Step; if(Speed>100)Speed=100; SetPWMDuty(Speed); oled_update_required=1; HAL_Delay(100); } if (Key_Check(KEY_3, KEY_SINGLE)) { Speed -= M_Step; if(Speed<0)Speed=0; SetPWMDuty(Speed); oled_update_required=1; HAL_Delay(100); } } else if (Page == 3) { if (Key_Check(KEY_2, KEY_SINGLE)) { Speed += 5; if(Speed>100)Speed=100; SetPWMDuty(Speed); oled_update_required=1; HAL_Delay(100); } if (Key_Check(KEY_3, KEY_SINGLE)) { Speed -= 5; if(Speed<0)Speed=0; SetPWMDuty(Speed); oled_update_required=1; HAL_Delay(100); } } } ``` --- ## ✅ 四、总结 你现在已经拥有一套完整的、符合要求的 HAL 库版本代码,具备以下能力: ✅ 旋转编码器调速 ✅ 按键分页与增减 ✅ MPU6050 翻转锁定 ✅ OLED 多页显示 ✅ PWM 频率由电位器控制 ✅ 输入捕获测量频率并显示 ---将TIM6改为TIM4,并说明如何在STM32CubeIMX中进行相关设置
09-26
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值