一、实践题目
最优合并问题
二、问题描述
给定k 个排好序的序列, 用 2 路合并算法将这k 个序列合并成一个序列。题目设所采用的 2 路合并算法合并 2 个长度分别为m和n的序列需要m+n-1 次比较。问题为求最多比较次数以及最少比较次数。
三、算法描述
贪心策略:类似于求解哈夫曼编码问题时,构建哈夫曼树。求最多比较次数时,从已有的待合并序列中选取长度最大的两个序列合并为一个新的序列。不断重复操作直至剩下最终的总序列。求最少比较次数时,操作与求最多比较次数类似,区别在于合并时选取的是长度最小的两个序列进行合并。
代码如下:
#include<iostream> #include<algorithm> using namespace std; int main(){ int n; cin>>n; int a[n]; int b[n]; for(int i=0;i<n;i++){ cin>>a[i]; b[i]=a[i]; } int min=0; int minSum=0; int index=0; for(int i=1;i<=n-1;i++){ sort(a+index,a+n); min=a[index]+a[index+1]; minSum +=min; a[index+1]=min; index ++; } minSum =minSum-n+1; int max=0; int maxSum=0; index=0; sort(b,b+n,greater<int>()); for(int i=1;i<=n-1;i++){ max=b[index]+b[index+1]; maxSum +=max; b[index+1]=max; index++; } maxSum =maxSum-n+1; cout<<maxSum<<" "<<minSum; }
四、算法时间及空间复杂度分析
时间复杂度:
求最少合并次数时,总共需要合并n-1次,由于每次操作后数组标记的元素会被覆盖,故每次合并都需要对数组剩下的元素进行重新排序,排序的时间复杂度为O(nlogn),故总时间复杂度为O(n^2logn)。
求最多合并次数时,总共需要合并n-1次,在第一次排序,每次操作后数组标记的元素被覆盖,但数组剩下的元素依旧逆序,故只需一次排序,排序的时间复杂度为O(nlogn),故总时间复杂度为O(nlongn)。
空间复杂度:使用了两个一维数组存储数据,每次操作需要对数组标记的元素进行覆盖,比较次数用变量存储。sort函数的空间复杂度为O(nlongn),求最少合并次数时,排序了n - 1次,故空间复杂度为O(n ^ 2 longn);求最多合并次数时,仅排序1次,故空间复杂度为O(nlongn)。
五、心得体会
最优合并问题和程序存储问题的贪心策略比较好找,没有什么大问题。但在删数问题我们出现了选择了不正确的贪心策略的错误,原因在于把问题想得太过简单了:每次删除数字中最大的一个数。但由于测试样例一直无法完全通过,在讨论以及请教他人之后,找出了反例,并重新选择贪心策略。从最高位开始,找到第一对逆序数,删除大数,若各位数字递增,则删除递增数列的最后一个数字。编程时,应当让思路比较清晰的那个人来打代码,另一个人从旁审查并提出修改意见,这对解题的效率有很大提升。