引言
本次内容包含第四章分治策略中算法的代码实现
最大子数组问题
最大子数组问题是寻找一个数组A中最大的非空连续子数组。
文中提到了三种解决方法,分别是暴力求解法,分治策略,和课后习题中的线性解决方法(动态规划)。下面给出分治策略和动态规划的代码实现。
库引用和结构体定义
#define LEN_OF_ARRAY(a) (sizeof(a) / sizeof(a[0]))
#include <stdio.h>
#include <math.h>
#include <limits.h>
typedef struct
{
int low;
int high;
int sum;
}SubArray;
分治策略
// *********** merge ************
SubArray FindMaxCrossSubarray(int A[], int high, int mid, int low){
int sum = 0;
int index = mid;
int maxSumLeft = INT_MIN;
int maxLeft;
while(index >= low){
sum += A[index-1];
if(maxSumLeft < sum){
maxSumLeft = sum;
maxLeft = index;
}
index--;
}
sum = 0;
index = mid+1;
int maxSumRight = INT_MIN;
int maxRight;
while(index <= high){
sum += A[index-1];
if(maxSumRight < sum){
maxSumRight = sum;
maxRight = index;
}
index++;
}
return SubArray{maxLeft, maxRight, maxSumLeft+maxSumRight};
}
SubArray FindMaxSubarrayMerge(int A[], int high, int low=1){
if(low == high)
return SubArray{low, high, A[low-1]};
int mid = floor((high + low) / 2);
SubArray subLeft = FindMaxSubarrayMerge(A, mid, low);
SubArray subRight = FindMaxSubarrayMerge(A, high, mid+1);
SubArray subMid = FindMaxCrossSubarray(A, high, mid, low);
if(subLeft.sum > subMid.sum && subLeft.sum > subRight.sum)
return subLeft;
if(subRight.sum > subLeft.sum && subRight.sum > subMid.sum)
return subRight;
return subMid;
}
动态规划
其中sumToMax[ i ]记录的是以 i 为底的最大连续子数组
当sumToMax[ i-1 ] <0时,A数组的前 i 项就对sumToMax数组后 i 项的求值没有影响了
// *********** line dynamic programming ************
SubArray FindMaxSubarrayDynamic(int A[], int len){
int sumMax = A[0];
int start = 0;
int end = 0;
int temIndex = 0;
int sumToIMax[len];
sumToIMax[0] = A[0];
for(int i=1; i<len; i++){
if(sumToIMax[i-1] <= 0){
sumToIMax[i] = A[i];
temIndex = i;
}else{
sumToIMax[i] = A[i] + sumToIMax[i-1];
}
if(sumToIMax[i] > sumMax){
start = temIndex;
end = i;
sumMax = sumToIMax[i];
}
}
return SubArray{start+1, end+1, sumMax};
}
主测试函数
int main(){
int a[] = {6,3,4,-1,-5,-6,20,-9,3};
// int a[] = {-3,-4};
SubArray s = FindMaxSubarrayMerge(a, LEN_OF_ARRAY(a));
printf("%d, from %d to %d\n", s.sum, s.low, s.high);
s = FindMaxSubarrayDynamic(a, LEN_OF_ARRAY(a));
printf("%d, from %d to %d\n", s.sum, s.low, s.high);
return 0;
}
芯片检测问题(4-5)
Diogenes 教授有n个被认为是完全相同的集成电路芯片,原理上它们是可以互相测试的。教授的测试装置一次可容纳二片芯片,当该装置中放有两片芯片时,每片芯片就对另一片进行测试,并报告其好坏。一个好的芯片总能够正确的报告另一片的好坏,但一个坏的芯片的结果是不可靠的。每次的测试的四种可能结果如下:
A芯片报告 | B芯片报告 | 结论 |
---|---|---|
B是好的 | A是好的 | 都是好的,或都是坏的 |
B是好的 | A是坏的 | 至少一片是坏的 |
B是坏的 | A是好的 | 至少一片是坏的 |
B是坏的 | A是坏的 | 至少一片是坏的 |
a)证明若不少于 n/2 的芯片是坏的,在这种成对测试方式下,使用任何策略都不能确定哪个芯片是好的.
b)假设有多于 n/2 的芯片是好的,考虑从 n 片中找出一片好芯片的问题.证明 n/2 对测试就足以使问题的规模降至近原来的一半.
c)假设有多于 n/2 的芯片是好的,证明好的芯片可用 O(n) 对测试找出.给出并解答表达测试次数的递归式。
- a) 设有a块好芯片,a+m块坏芯片。其中a块坏芯片将其他的n-a块芯片分成坏的,剩下的m块坏芯片随机分好坏,则无法分开a块好芯片和a块坏芯片。
- b) c)
- n个芯片随机成对测试,总共n/2次,若是后三种情况,两片都丢弃,第四种情况则随机丢弃一片芯片。若是奇数个芯片,则将剩余的那个芯片与每个芯片测试,若有半数为真,则返回该芯片,否则丢弃,花费n次操作。
- 因为属于后三种情况的芯片中坏芯片数 >= 好芯片数,所以属于第一种情况的芯片依然满足好芯片数 > 坏芯片数。又因为第一种情况相当于好芯片、坏芯片各自随机丢弃一半的数量,所以剩下的芯片依然满足好芯片数 > 坏芯片数,并且芯片数量减少了至少n/2,成为了规模为之前一半的子问题。再回到1的操作直到只剩一个芯片。
- 最糟糕情况下,算法的复杂度的递归式为T(n) = T(n/2) + 3n/2,则T(n) = O(n)