7-1 最优合并问题
问题描述:
给定k 个排好序的序列, 用 2 路合并算法将这k 个序列合并成一个序列。 假设所采用的 2 路合并算法合并 2 个长度分别为m和n的序列需要m+n-1 次比较。试设 计一个算法确定合并这个序列的最优合并顺序,使所需的总比较次数最少。 为了进行比较,还需要确定合并这个序列的最差合并顺序,使所需的总比较次数最多。
输入格式:
第一行有 1 个正整数k,表示有 k个待合并序列。 第二行有 k个正整数,表示 k个待合并序列的长度。
输出格式:
输出最多比较次数和最少比较次数。
算法描述:
对于最少比较次数:将待合并序列按照序列长度从小到大排序,优先合并两个最小长度的序列,之后将这两个序列的长度相加合并成一个具有新长度的序列,重新排序,之后采取相同的贪心策略
对于最多比较次数:将待合并序列按照序列长度从大到小排序,优先合并两个最大长度的序列,之后将这两个序列的长度相加合并成一个具有新长度的序列,之后采取相同的贪心策略,无需重新排序
代码实现:
1 #include <iostream> 2 #include<algorithm> 3 using namespace std; 4 bool cmp(int a,int b) 5 { 6 return a>b; 7 } 8 int maxsum(int a[],int k){ 9 sort(a,a+k,cmp);//排序 10 int maxsum=a[0]+a[1]-1; 11 int temp=a[0]+a[1]-1; 12 for(int i=2;i<k;i++){ 13 temp+=a[i];//记录新一轮计算的次数 14 maxsum+=temp;//和上一轮合并 15 } 16 return maxsum; 17 18 } 19 int minsum(int a[],int k){ 20 sort(a,a+k);//排序 21 int minsum = 0; 22 for(int i=0;i<k-1;i++){ 23 minsum+=a[i]+a[i+1]-1;//第0个和第1个为最小的两个 24 a[i]=a[i]+a[i+1];//合并成新长度的序列 25 a[i+1]=0;//另一个序列的长度置为0 26 sort(a,a+k);//重新排序 27 } 28 return minsum; 29 } 30 int main(){ 31 int k; 32 cin>>k; 33 int a[k],b[k]; 34 for(int i=0;i<k;i++){ 35 cin>>a[i]; 36 b[i]=a[i];//复制另一个数组,两个数组分别求最小值最大值 37 } 38 39 cout<<maxsum(a,k)<<" "<<minsum(b,k); 40 return 0; 41 42 }
算法的时间和空间复杂度:
时间复杂度:
对于最少比较次数:由于每次合并两个长度之后都要重新进行排序,共排序n次,而每次排序的时间复杂度为O(nlogn),所以总的时间复杂度为O(n^2logn)
对于最多比较次数:由于不需要重新排序,所以总的时间复杂度为排序算法的时间复杂度O(nlogn)
空间复杂度:
算法用到两个一维数组,空间复杂度为O(1)
心得体会:
这道题目的贪心策略还是很容易想到的,问题在于我一开始解题的时候忽略了【比较次数最少】可能会出现两个最小长度的序列合并之后会比原来剩下序列的长度要大,所以并非永远都是合并完之后加上下一个元素就可以,而是需要重新排序。这一点是在上机的时候老师提醒我的,但是我自己编程序的时候又没有仔细地去考虑【比较次数最多】的情况,以为【最少】和【最多】都需要重新排序,但是后来我一想,两个最大长度的序列合并之后肯定不会出现比剩余序列的长度大的情况啊(一个都已经比剩下的大了,更何况两个的和呢?),所以【最多】是不需要重新排序的,虽然重新排序在PTA上也能过,但很明显重新排序会耗费更多的时间,这不是我们想要的,所以能避免我们当然要尽量避免,这一点也是我在写实践报告分析时间复杂度的时候发现的,这让我收获到做完实验之后的总结反思很重要,就像我这道题,如果没有后面的实践报告,我可能就提交上去不管了,反正也能过,就不会去思考算法后续的优化问题了。