这个问题源自一个父子间的数学游戏场景:给定N个数字(可能包含正数、负数和零),需要找出连续M个数字(L1≤M≤L2)的和的最大值。这个看似简单的问题实际上考察了算法设计中的几个关键点:
实际应用中,这类问题常见于金融数据分析(如股票最佳买入时段)、信号处理等领域。例如分析某支股票连续N天的价格波动,寻找最佳买入窗口期。
原始代码采用了三重循环的暴力解法:
c复制for(int i=0;i<n;i++){ // 子段起点
for(int j=i;j<n;j++){ // 子段终点
int len=j-i+1; // 计算当前子段长度
if(len>=L1&&len<=L2){
int sum=0;
for(k=i;k<=j;k++) sum+=arr[k];
if(sum>maxsum) maxsum=sum;
}
}
}
时间复杂度分析:
优化思路:
c复制prefix[0] = arr[0];
for(int i=1;i<n;i++)
prefix[i] = prefix[i-1] + arr[i];
c复制// 计算arr[i..j]的和
sum = prefix[j] - (i>0 ? prefix[i-1] : 0);
优化后复杂度降为O(n²)
注意:当n较大时(>10⁴),应考虑更优的滑动窗口或动态规划解法
这个问题要求统计所有和能被11整除的连续子数组数量。与最大和问题不同,这里需要完整遍历所有可能的子数组。
关键实现细节:
c复制for(int i=0;i<n;i++){ // 起点
for(int j=i;j<n;j++){ // 终点
int sum=0; // 必须在此处初始化
for(int k=i;k<=j;k++) sum+=arr[k];
if(sum%11==0) count++;
}
}
易错点警示:
利用前缀和+哈希表可将复杂度优化到O(n):
c复制// 初始化
int remainder = 0, count = 0;
int mod_map[11] = {0};
mod_map[0] = 1; // 前缀和本身能被11整除的情况
for(int i=0;i<n;i++){
remainder = (remainder + arr[i]) % 11;
if(remainder < 0) remainder += 11; // 处理负数
count += mod_map[remainder];
mod_map[remainder]++;
}
问题描述:用最多M块木板覆盖所有有牛的牛棚,求最小总长度。这是典型的区间覆盖问题,贪心策略如下:
c复制qsort(arr, c, sizeof(int), compare_asc); // 牛棚排序
int total = arr[c-1] - arr[0] + 1; // 全覆盖长度
// 计算间距
for(int i=0;i<c-1;i++)
gaps[i] = arr[i+1] - arr[i] - 1;
qsort(gaps, c-1, sizeof(int), compare_desc); // 间距排序
int saved = 0;
for(int i=0;i<m-1;i++) saved += gaps[i]; // 取前M-1大间距
特殊场景需要考虑:
原始方案采用标记法(时间复杂度O(n²)):
c复制for(int i=0;i<n;i++){
if(arr[i]!=-1){
for(int j=i+1;j<n;j++){
if(arr[j]==arr[i]) arr[j]=-1;
}
}
}
更优方案(O(nlogn)):
c复制qsort(arr, n, sizeof(int), compare_asc);
int k=0;
for(int i=1;i<n;i++){
if(arr[i]!=arr[k]) arr[++k]=arr[i];
}
// 有效元素为arr[0..k]
这种处理方式常见于:
这种古老的乘法方法实际上是俄罗斯农民算法的变种,其数学原理基于:
code复制a × b = (a/2) × (b×2) 当a为偶数
a × b = (a-1)/2 × (b×2) + b 当a为奇数
实现要点:
c复制while(a > 0){
if(a % 2 == 1){
printf("%d",b);
sum += b;
if(a > 1) printf("+");
}
a /= 2;
b *= 2;
}
虽然这种算法在现代CPU中并不直接使用,但其变体出现在:
避免重复计算:
合理选择数据结构:
数学性质利用:
常见问题排查方法:
c复制assert(L1 <= L2 && L2 <= n);
将算法题代码转化为生产代码需要考虑:
建议深入学习:
我在实际编码中发现,理解问题本质比记忆算法模板更重要。比如部落乘法问题,如果理解其二进制分解的本质,就能轻松处理各种变体。建议初学者多画图分析,建立直观理解后再编码。