修理牛棚问题可以抽象为一个典型的区间覆盖优化问题。给定一系列离散的点(有牛的牛棚编号),我们需要用最少数量的木板(不超过M块)覆盖所有这些点,同时使木板总长度最小。
这个问题可以转化为:在数轴上给定C个点,用不超过M个连续区间覆盖所有点,使得区间总长度最小。关键在于如何选择区间的断点位置。
核心思路是将"最小化木板总长度"转化为"最大化可以节省的长度"。具体步骤如下:
计算初始总长度:如果用一块木板覆盖所有牛棚,长度为cows[C-1] - cows[0] + 1
计算相邻牛棚间的空隙:相邻牛棚间的空隙为cows[i] - cows[i-1] - 1
排序空隙:将空隙按从大到小排序
选择最大空隙:使用M-1个最大空隙(因为M块木板可以形成M-1个间隔)
计算最终长度:初始总长度减去选中的空隙总和
cpp复制#include<bits/stdc++.h>
using namespace std;
int main(){
int m,c;
cin>>m>>c;
vector<int>cows(c);
for(int i=0;i<c;i++){
cin>>cows[i];
}
// 特殊情况处理:只有一头牛
if(c==1){
cout<<1;
return 0;
}
// 对牛棚编号排序
sort(cows.begin(),cows.end());
// 计算相邻牛棚间的空隙
vector<int>gaps;
for(int i=1;i<c;i++){
int d = cows[i] - cows[i-1] - 1;
if(d > 0){
gaps.push_back(d);
}
}
// 对空隙从大到小排序
sort(gaps.begin(),gaps.end(),greater<int>());
// 计算可以节省的最大长度
int num_gaps = min((int)gaps.size(), m-1);
int saved = 0;
for(int i=0;i<num_gaps;i++){
saved += gaps[i];
}
// 计算最终结果
int total = (cows.back()-cows.front()+1) - saved;
cout<<total;
return 0;
}
未处理单牛情况:当只有一头牛时,直接输出1,否则会导致数组越界
空隙计算错误:忘记减1导致空隙计算错误(两个相邻牛棚的空隙应为0)
排序方向错误:空隙应从大到小排序才能优先使用最大空隙
木板数量限制:实际可用的空隙数为min(gaps.size(), m-1)
调试技巧:可以打印中间变量如空隙数组、节省的长度等,验证计算过程是否正确。
部落人乘法是一种古老的乘法算法,其本质是利用二进制分解的思想。以13×15为例:
算法通过不断除以2(右移)和乘以2(左移)来实现这一过程,舍弃偶数行相当于忽略二进制表示中的0。
cpp复制#include<bits/stdc++.h>
using namespace std;
int main(){
int a,b;
while(cin>>a>>b){
cout<<a<<"*"<<b<<"=";
int sum = 0;
int original_a = a; // 保存原始a值用于输出
while(a > 1){
if(a%2 == 1){
cout<<b<<"+";
sum += b;
}
a /= 2;
b *= 2;
}
sum += b;
cout<<b<<"="<<sum<<endl;
}
return 0;
}
输出格式优化:可以先将所有要加的数存储在vector中,最后统一输出,避免处理末尾的"+"
递归实现:可以用递归函数替代循环结构
性能分析:时间复杂度为O(logN),空间复杂度O(1),非常高效
这个问题要求将k个小球分配到尽可能多的盒子中,满足:
这等价于寻找最长的严格递增正整数序列,其和为k,且序列尽可能"平滑"。
cpp复制#include<bits/stdc++.h>
using namespace std;
int main(){
int k;
while(cin>>k){
// 1. 找出最大的m使得1+2+...+m <= k
int m = 0, sum = 0;
while(sum + m + 1 <= k){
m++;
sum += m;
}
// 2. 计算剩余小球数
int d = k - sum;
// 3. 构造初始序列1,2,...,m
vector<int> ans(m);
for(int i=0; i<m; i++){
ans[i] = i+1;
}
// 4. 从后往前分配剩余小球
for(int i=m-1; i>=m-d; i--){
ans[i]++;
}
// 5. 输出结果
for(int i=0; i<m; i++){
if(i>0) cout<<',';
cout<<ans[i];
}
cout<<endl;
}
return 0;
}
A-B(差集)定义为属于A但不属于B的所有元素。数学表示为:A - B =
cpp复制#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
int n,m;
cin>>n>>m;
set<int> A,B;
int x;
// 读取集合A
for(int i=0;i<n;i++){
cin>>x;
A.insert(x);
}
// 读取集合B
for(int i=0;i<m;i++){
cin>>x;
B.insert(x);
}
// 计算A-B
vector<int> result;
for(int a : A){
if(B.find(a) == B.end()){
result.push_back(a);
}
}
// 输出结果
if(result.empty()){
cout<<"NULL"<<endl;
}else{
for(int x : result){
cout<<x<<' ';
}
cout<<endl;
}
}
return 0;
}
输入优化:对于大规模数据,可以使用更快的输入方法如scanf
输出优化:对于大量输出,可以考虑使用'\n'代替endl避免频繁刷新缓冲区
空间优化:如果内存紧张,可以边读入边处理,不存储全部元素
在实际编程竞赛中,理解这些经典问题的解法并能够快速实现是提高竞争力的关键。每个问题都展示了不同的算法思想和编程技巧,值得反复练习和掌握。