【解法1】暴力解:枚举最小正整数
原始讲义 · 非增强校订版 · 原书检索 · 文字用于定位,公式与图表请回原页核验 · 本次导入快照
## 【解法1】暴力解:枚举最小正整数
(1)可以证明最小未出现的正整数t一定≤n+1(因为如果t>n+1,则1\~t-1都会出现一次,但只有n个整数,所以t-1≤n,即t≤n+1),枚举所有可能的未出现最小正整数,扫描一次数组判断是否符合。
(2) 伪代码如下:
int ans(int A[], int n)
{
for (int i=1; i<=n+1; i++)
{
bool flag=false;
for (int j=0; j<n; j++)
if (i==A[j])
flag=true;
if (flag==false)
return i;
}
}
// 先假设i未出现
//此时i出现了,f1ag变成true
//若i未出现过则输出i
(3) // 时间复杂度:O(n²) 空间复杂度:O(1)
## 【解法2】暴力解:快速排序,然后扫描一遍数组
(1)先对数组A快速排序得到升序序列,然后遍历数组找到第一个未出现的最小正整数。
(2) 伪代码如下:
void Qsort(int A[], int L, int R)
{N
if (L>=R)
return;
int pivot, i=L, j=R;
}
// 当前区间元素个数<=1则退出
// i和j是左右两个数组下标移动
<!-- page 66 -->
pivot=A[L]; // pivot作为基准值参与比较
while (i<j){
while (i<j && A[j]>=pivot)
j--;
while (i<j && A[i]<=pivot)
i++;
if (i<j)
swap(A[i], A[j]); // A[j]<pivot, A[i]>pivot
}
swap(A[L], A[i]); //将基准值放入他的最终位置
Qsort(A, L, i-1); //递归处理左区间
Qsort(A, i+1, R); // 递归处理右区间
int ans(int A[], int n)
{
Qsort(A, 0, n-1);
int t=1; //假设初始最小未出现正整数t=1
for (int i=0; i<n; i++)
if (A[i]==t)
t++; // t已出现,此时最小未出现正整数是t+1
return t; //输出最小未出现正整数
}
(3) // 时间复杂度:O(nlogn) 空间复杂度:O(logn)
## 【解法3】优化解:散列表
(1)可以证明最小未出现的正整数t一定≤n+1(因为如果t>n+1,则1\~t-1都会出现一次,但只有n个整数,所以t-1≤n,即t≤n+1),设置一个数组count,count[i]表示i是否在原数组A中出现过,扫描一遍数组A,更新count数组,之后再扫描一遍count数组输出未出现的最小正整数。
(2) 伪代码如下:
int ans(int A[],int n)
{
int count[n+2]={0}; // 0~n+1
for (int i=0; i<n; i++) // 扫描数组A
if (A[i]>0 && A[i]<=n+1) // 当0<A[i]<=n+1时才修改
count[A[i]]++;
for (int j=1; j<=n+1; j++) //扫描count数组
if (count[j]==0) // j未出现过,是最小未出现正整数
return j; //输出j
}
(3) // 时间复杂度:O(n) 空间复杂度:O(n)
<!-- page 67 -->
(13 分)定义三元组(a,b,c)(a,b,c 均为正数)的距离 D = |a - b| + |b - cl + |c - al。给定 3 个非空整数集合 S1、S2和S3,按升序分别存储在3 个数组中。请设计一个尽可能高效的算法,计算并输出所有可能的三元组(a,b,c)(a∈S1,b∈S2,c∈S3) 中的最小距离。例如S1 = {-1,0, 9},S2= {-25, -10, 10,11},S3 ={2,9, 17, 30,41},则最小距离为2,相应的三元组为(9,10,9)。要求:
(1)给出算法的基本设计思想。
(2)根据设计思想,采用C或C++语言描述算法,关键之处给出注释。
(3)说明你所设计算法的时间复杂度和空间复杂度。
<table><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=1>做题思路</td><td rowspan=1 colspan=1>时间复杂度</td><td rowspan=1 colspan=1>空间复杂度</td><td rowspan=1 colspan=1>预计得分</td></tr><tr><td rowspan=1 colspan=1>暴力解</td><td rowspan=1 colspan=1>枚举</td><td rowspan=1 colspan=1>O(nml)</td><td rowspan=1 colspan=1>0(1)</td><td rowspan=1 colspan=1>10</td></tr><tr><td rowspan=1 colspan=1>优化解</td><td rowspan=1 colspan=1>枚举+多指针法</td><td rowspan=1 colspan=1>O(mn)</td><td rowspan=1 colspan=1>O(1)</td><td rowspan=1 colspan=1>11</td></tr><tr><td rowspan=1 colspan=1>优化解</td><td rowspan=1 colspan=1>枚举+折半查找</td><td rowspan=1 colspan=1>O(n * log(m * l))</td><td rowspan=1 colspan=1>O(1)</td><td rowspan=1 colspan=1>12</td></tr><tr><td rowspan=1 colspan=1>优化解</td><td rowspan=1 colspan=1>多指针法</td><td rowspan=1 colspan=1>O(m + n + l)</td><td rowspan=1 colspan=1>O(1)</td><td rowspan=1 colspan=1>13</td></tr></table>
【解法1】暴力解:枚举,三重循环将abc都扫描一次
1)数组A、B、C的长度分别为n、m、I,三重循环枚举a、b、c,对于每一组a、b、c求出对应的距离D,并从中选出最小的距离。
2)伪代码如下:
int ans(int A[],int n,int B[],int m,int C[],int l)
{
int D_min=INT_MAX, D; // D_min初值为最大整数
for (int i=0; i<n; i++)
for (int j=0; j<m; j++)
for (int k=0; k<1; k++){ // 三重循环枚举abc
D=abs(A[i]-B[j])+abs(B[j]-C[k])+abs(C[k]-A[i]);
D_min=min(D_min, D); // 更新最小D值
}
return D_min; //输出最小D值
}
(3) 时间复杂度: O(nml)或O(n³) 空间复杂度:O(1)
【解法2】优化解:枚举+多指针法。两重循环扫描ab,而c数组指针后移
1)数组A、B、C的长度分别为n、m、I,两重循环枚举a、b,对数组C设置变量k存储数组下标不断后移,初始k=0,只要移动后的D更小,就选择往后移动,然后对于每一组a、b、c求出对应的距离D,并从中选出最小的距离。
int DD(int a,int b,int c)
{ // DD(a,b,c)=|a-b|+|b-c|+|b-a|
return abs(a-b)+abs(b-c)+abs(c-a);
}
int ans(int A[],int n,int B[],int m,int C[],int l)
{
int D, D_min=INT_MAX; // D_min初值为最大整数
int k=0;
for (int i=0; i<n; i++)
for (int j=0; j<n; j++)
{ //两重循环枚举a、b
D = DD(A[i], B[j], C[k]); //枚举a、b得到的初始D
while (k < l - 1 && DD(A[i], B[j], C[k+1])<D)
D = DD(A[i], B[j], C[++k]); // 更新下标k和距离D
D_min = min(D_min, D); // 更新最小D值
}
return D_min; // 输出最小D值
}
<!-- page 68 -->
(3) // 时间复杂度: O(mn) 或O(n²) 空间复杂度:O(1)
【解法3】优化解:枚举+折半查找。一重循环扫描a,两轮折半查找b、c
(1)本解法可以证明当a确定时,b只能是数组B中<a且最接近a的那个数或>=a且最接近a的那个数,c也只能是数组C中<a且最接近a的那个数或>=a且最接近a的那个数,所以只需要枚举a并折半查找确定b和c可能的值然后选择最小的D。数组A、B、C的长度分别为n、m、I,一重循环枚举a=A[i],通过两次折半查找可以查找到插入位点j和k保证B[j]<A[i]<B[j+1],C[k]<A[i]<C[k+1](如果插入位点是最左端和最右端另外判断),然后判断<A[i],B[i],C[k]>、<A[i],B[j+1].C[k]>、<A[i],B[i],C[k+1]>、<A[i],B[j+1],C[k+1]>哪种情况D最小即可,就能得到a确定的情况下最小可能的D,枚举a更新Dmin即可。最后输出最小值D min。
(2) 伪代码如下:
<!-- verified-by-hand -->
> [!success] 已结合原文第 68 页及 Markdown 上下文人工校对
> DD 函数缺少闭合花括号,导致 ans 被非法嵌套;数组 C 的长度 l 在 1-1、k+1<1 中被 OCR 成数字 1,并混入裸字‘道’。用契约明确的 lowerBound 返回首个 >=x 位置,再检查前驱和该位置,可完整覆盖四种候选。
int lowerBound(int A[], int n, int x)
{
int L = 0, R = n;
while (L < R)
{
int mid = L + (R - L) / 2;
if (A[mid] < x) L = mid + 1;
else R = mid;
}
return L;
}
int DD(int a, int b, int c)
{
return abs(a - b) + abs(b - c) + abs(c - a);
}
int ans(int A[], int n, int B[], int m, int C[], int l)
{
int D_min = INT_MAX;
for (int i = 0; i < n; i++)
{
int jb = lowerBound(B, m, A[i]);
int kc = lowerBound(C, l, A[i]);
for (int db = -1; db <= 0; db++)
for (int dc = -1; dc <= 0; dc++)
{
int j = jb + db, k = kc + dc;
if (j >= 0 && j < m && k >= 0 && k < l)
D_min = min(D_min, DD(A[i], B[j], C[k]));
}
}
return D_min;
}
(3) // 时间复杂度: O(n *(logm + logl))或O(nlogn) 空间复杂度:O(1)
<!-- page 69 -->
(1)数组A、B、C的长度分别为n、m、I,对数组A、B、C分别设置变量i、j、k存储数组下标不断后移,初始i=j=k=0,可以证明如果下标是往后移动,只有当移动A[i]、B[i]、C[k]最小的那个时,D=2L3才有可能减小,所以每次都只后移所指元素值最小的对应数组下标变量,一直重复进行,并更新D的最小值Dmin,直到所指元素值最小的对应数组无法在后移,最后输出最小值D\_min。
(2) 伪代码如下:
int DD(int a,int b,int c) // DD(a,b,c)=|a-b|+|b-c|+|b-a|
{
return abs(a-b)+abs(b-c)+abs(c-a);
}
int ans(int A[],int n,int B[],int m,int c[],int l)
{
int D_min=INT_MAX; // D_min初值为最大整数
int i=0, j=0, k=0; //数组A、B、C的下标i、j、k
while(i<n && j<m && k<1)
{
D_min=min(D_min,DD(A[i],B[j],C[k])); // 更新D_min
if (A[i]<B[j] && A[i]<C[k])
i++;
else
if (B[j]<C[k])
j++;
else
k++;
}
return D_min; // 输出最小D值
}
(3) // 时间复杂度: O(m + n + l)或O(n) 空间复杂度:O(1)
## 2025年 41题
(13 分)有两个长度均为 n的一维整型数组A[n]、res[n],计算 A[i] 与 A[i](0≤i≤j≤n-1)乘积的最大值,并将其保存到res[i] 中。若AΠ={1, 4, -9, 6},则得到 res[]={6,24,81,36}。现给定数组A,请设计时间空间上尽可能高效的算法CalMulMax,求 res 中各元素的值。函数原型为:void CalMulMax(int A[], int res[]. int n)。
(1)给出算法的基本思想。(4分)
(2) 用C/C++ 描述算法关键之处给出注释。(7 分)
(3)说明时间、空间复杂度。(2 分)
<table><tr><td rowspan=1 colspan=1></td><td rowspan=1 colspan=1>做题思路</td><td rowspan=1 colspan=1>时间复杂度</td><td rowspan=1 colspan=1>空间复杂度</td><td rowspan=1 colspan=1>预计得分</td></tr><tr><td rowspan=1 colspan=1>暴力解</td><td rowspan=1 colspan=1>枚举</td><td rowspan=1 colspan=1>O(n²)</td><td rowspan=1 colspan=1>0(1)</td><td rowspan=1 colspan=1>10</td></tr><tr><td rowspan=1 colspan=1>优化解</td><td rowspan=1 colspan=1>预处理</td><td rowspan=1 colspan=1>O(n)</td><td rowspan=1 colspan=1>O(n)</td><td rowspan=1 colspan=1>12</td></tr><tr><td rowspan=1 colspan=1>优化解</td><td rowspan=1 colspan=1>预处理</td><td rowspan=1 colspan=1>O(n)</td><td rowspan=1 colspan=1>0(1)</td><td rowspan=1 colspan=1>13</td></tr></table>
<!-- page 70 -->
【解法1】暴力解(双重循环枚举,10分左右
1)算法的基本思想
使用两个嵌套的for循环遍历数组A的所有可能的(i, j)组合,其中0 ≤i ≤j ≤ n-1。对于每个i,遍历所有j≥i,计算A[i]\*A[j],并记录其中的最大值到res[i]中。
2)C代码实现
// 暴力破解法实现
void CalMulMax(int A[], int res[], int n)
{
for(int i = 0; i < n; i++)
{ //遍历每个起始位置
o m
int maxMu] = A[i] * A[i]; //初始化
for(int j = i; j < n; j++)
{ // 遍历所有j >= i
int currentMu] = A[i] * A[j];
if (currentMul > maxMul) //当前乘积更大,更新
maxMul = currentMul;
res[i] = maxMul; //将最大乘积存入res[i]
}
输出数组res
}
}
3)时间复杂度:O(n²),因为使用了两个嵌套的for循环。
空间复杂度:O(1),使用了常数级别的额外空间。
【解法2】预处理,使用辅助数组B和C(12分左右)
1)算法的基本思想观察可知:
如果 A[i] 是正数,想要乘积最大,自然希望乘以区间内最大的数;
• 如果 A[i] 是负数,想要乘积最大,需要乘以区间内最小的数(即最小的负数,两个负数相乘为正,值越大)。
因此,对于每个位置 i,提前预处理出:
• 从位置 i 到末尾的最大值 B[i];
• 从位置 i 到末尾的最小值 Ci。
//使用辅助数组B和C的实现
void CalMulMax(int A[], int res[], int n)
{
int B[n];
int c[n];
// 从后向前遍历,填充B和C数组
B[n-1] = A[n-1];
C[n-1] = A[n-1];
for(int i = n-2; i >= 0; i--)
{
B[i] = max(B[i+1], A[i]);
c[i] = min(c[i+1], A[i]);
}
//计算res数组,最大值只可能是A[i]*B[i]或A[i]*C[i]
for(int i = 0; i < n; i++)
res[i] = max(A[i]*B[i], A[i]*C[i]);
输出数组res
}
<!-- page 71 -->
3)时间复杂度:O(n),因为预处理B和C数组各需一次线性遍历,计算res数组也只需一次线性遍历。
空间复杂度:O(n),需要额外的两个辅助数组B和C,每个大小为n。
【解法3】预处理,使用辅助变量B和C(13分左右)
1)算法的基本思想
只需要在解法二的基础上,用变量存最小值和最大值即可。
2)C代码实现
//使用辅助变量B和C的实现
void CalMulMax(int A[], int res[], int n)
{
int B; // B表示A[i..n-1]中的最大值
int C; // C表示A[i..n-1]中的最小值
B = A[n-1];
C = A[n-1];
res[n-1] = A[n-1]*A[n-1];
//计算res数组,最大值只可能是A[i]*B[i]或A[i]*C[i]
for(int i = n-2; i >= 0; i--)
{
B = max(B, A[i]);
C = min(C, A[i]);
res[i] = max(A[i]*B, A[i]*C);
}
输出数组res
}
<!-- code-audit: false_positive -->
> [!info] 已人工审读:这是题解复杂度文字,不是程序语句;解法的 O(n) 时间、O(1) 额外空间结论与紧邻代码一致。
```c
3)时间复杂度:O(n)。 空间复杂度:O(1)。
```