贪心——最优合并问题

1.最优合并问题

Description

给定k 个排好序的序列s1 , s2,……, sk , 用2 路合并算法将这k 个序列合并成一个序列。假设所采用的2 路合并算法合并2 个长度分别为m和n的序列需要m + n -1次比较。试设计一个算法确定合并这个序列的最优合并顺序,使所需的总比较次数最少。
为了进行比较,还需要确定合并这个序列的最差合并顺序,使所需的总比较次数最多。
对于给定的k个待合并序列,计算最多比较次数和最少比较次数合并方案。
 

Input

输入数据的第一行有1 个正整数k(k≤1000),表示有k个待合并序列。接下来的1 行中,有k个正整数,表示k个待合并序列的长度。

Output

输出两个整数,中间用空格隔开,表示计算出的最多比较次数和最少比较次数。

Samples

Sample #1
Input 
4
5 12 11 2
Output 
78 52

2.代码

#include<bits/stdc++.h>
using namespace std;
const int N=1004;
int a[N],b[N];
bool cmp(int e,int f)
{
	return e>f;
}
int main()
{
	int k;
	cin>>k;
	for(int i=0;i<k;i++)
	{
		cin>>a[i];
		b[i]=a[i];
	}
	sort(a,a+k);
	sort(b,b+k,cmp);
	int minn=0,maxn=0;
	for(int i=0;i<=k-2;i++)
	{
		a[i+1]+=a[i];
		minn+=a[i+1]-1;
		sort(a+i+1,a+k);
		b[i+1]+=b[i];
		maxn+=b[i+1]-1;
		sort(b+i+1,b+k,cmp);
	}
	cout<<maxn<<" "<<minn<<endl;
	return 0;
}

3.代码解析

这个问题实际上是经典的哈夫曼编码问题,也称为最优合并问题。
   (1)使用std::sort 对数组 a 进行升序排序,这样最小的序列就在数组的前面。
   使用自定义的比较函数 cmp对数组 b 进行降序排序,这样最大的序列就在数组的前面。
   (2)计算最少比较次数(minn)
   通过循环,每次合并两个最小的序列(a[i] 和 a[i+1]),合并后的序列长度为 a[i+1] += a[i],并且增加比较次数 minn。每次合并后,更新 a数组,并将合并后的序列放到数组的前面,然后对剩余序列重新进行升序排序。
    (3)计算最多比较次数(maxn)
   类似地,每次合并两个最大的序列(b[i]和 b[i+1]),合并后的序列长度为 b[i+1] += b[i],并且增加比较次数 maxn。每次合并后,更新 b`数组,并将合并后的序列放到数组的前面,然后对剩余序列重新进行降序排序。最后,输出最多比较次数 maxn 和最少比较次数 minn。
    (4)思想
贪心策略:这个问题使用了贪心算法的思想,即在每一步选择中都采取在当前状态下最好或最优的选择,希望这样能导致全局最优解。

4.扩列

(1)sort 函数

在C++中,std::sort 函数是一个非常强大的标准库算法,用于对序列进行排序。它接受三个参数:

要排序的序列的起始迭代器。
要排序的序列的结束迭代器。
一个可选的比较函数或函数对象,它定义了序列元素的排序准则。

(2)cmp 函数的用法


文章中的 cmp 函数是一个比较函数,它接受两个参数 e 和 f,并返回一个布尔值。在这个函数中,它检查 e 是否大于 f。如果 e 大于 f,函数返回 true;否则返回 false。这个函数通常用于 std::sort 的第三个参数,以实现降序排序。


(3)sort 函数的用法


 sort(a, a+k);:这行代码对数组 a 中的前 k 个元素进行升序排序。因为这里没有提供比较函数,所以 sort 使用默认的比较函数,即小于号 <,这意味着元素将按照从小到大的顺序排列,升序。
sort(b, b+k, cmp);:这行代码对数组 b 中的前 k 个元素进行排序,但是使用的是比较函数 cmp。由于 cmp 定义为 e > f,所以这将导致数组 b 按照从大到小的顺序排列,降序。
  在C++标准库中的 std::sort`函数中,区间的表示使用的是左闭右开区间。这意味着区间包括起始位置,但不包括结束位置。具体到 sort(arr, arr+4);`这个调用,它的含义如下:
arr是区间的起始迭代器,表示排序的开始位置。
arr+4是区间的结束迭代器,表示排序的结束位置。
因此,sort(arr, arr+4); 会对数组 arr`的前4个元素进行排序,即索引为0、1、2、3的元素。这是一个左闭右开区间,所以包括索引0的元素,但不包括索引4的元素(即数组的第五个元素)。
 为什么使用左闭右开区间
左闭右开区间的设计有几个优点:
1. 避免越界:使用左闭右开区间可以避免在迭代时越界。
2. 灵活性:这种区间表示允许你轻松地选择子数组进行操作,而不需要额外的逻辑来处理边界条件。
3.一致性:在C++标准库的其他算法中也广泛使用了这种区间表示,保持了语言的一致性。

例如,如果你有一个数组 arr,你可以通过 arr + arr.size()来得到不越界的结束迭代器。 一致性:在C++标准库的其他算法中也广泛使用了这种区间表示,保持了语言的一致性。
示例
假设有一个数组 arr,其元素如下:
int arr[] = {10, 20, 30, 40, 50, 60, 70, 80, 90};
当你调用 sort(arr, arr+4); 时,排序的结果是:
int sortedArr[4] = {10, 20, 30, 40};
只有数组的前四个元素被排序,结果是 {10, 20, 30, 40},而数组的其余部分保持不变。这种区间表示确保了排序操作不会影响数组的其他部分,也不会导致数组越界。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值