数据结构PTA习题:基础实验4-2.7 修理牧场 (25分)

基础实验4-2.7 修理牧场 (25分)

农夫要修理牧场的一段栅栏,他测量了栅栏,发现需要N块木头,每块木头长度为整数L​i​​个长度单位,于是他购买了一条很长的、能锯成N块的木头,即该木头的长度是L​i​​的总和。
但是农夫自己没有锯子,请人锯木的酬金跟这段木头的长度成正比。为简单起见,不妨就设酬金等于所锯木头的长度。例如,要将长度为20的木头锯成长度为8、7和5的三段,第一次锯木头花费20,将木头锯成12和8;第二次锯木头花费12,将长度为12的木头锯成7和5,总花费为32。如果第一次将木头锯成15和5,则第二次锯木头花费15,总花费为35(大于32)。
请编写程序帮助农夫计算将木头锯成N块的最少花费。

输入格式:
输入首先给出正整数N(≤10^​4​​),表示要将木头锯成N块。第二行给出N个正整数(≤50),表示每段木块的长度。

输出格式:
输出一个整数,即将木头锯成N块的最少花费。

输入样例:

8
4 5 1 2 1 3 1 1

输出样例:

49

我用的方法是:先用输入数据点构造哈夫曼树,再前序遍历所有非叶子结点,求所有非叶结点的数据之和。
该过程需要构造一个最小堆,最小堆内存的是中间过程产生的二叉树根结点地址,当最小堆内只剩下一个二叉树时,该二叉树就是哈夫曼树。
下面举例说明:
4 5 1 2 1 3 1 1
每个数据变成只有1个根结点的二叉树,逐个插入最小堆,形成:
1 1 1 1 2 4 3 5(从数组下标为1的开始存起)
每次找出序列中最小和次小的两个数,构成二叉树,其根结点是两个数之和,并将形成的二叉树插回最小堆:
在这里插入图片描述
将哈夫曼树中的所有非叶结点的值相加18+8+10+4+5+2+2=49
C语言实现:

#include<stdio.h>
#include<stdlib.h>
struct node
{
 int data;
 struct node * left;
 struct node * right;
};
typedef struct node * Bintree;
struct heap
{
 Bintree * d;
 int size;
};
typedef struct heap * Heap;
Heap Insert(Heap H, Bintree B);//向最小堆内插入元素
Bintree Delete(Heap H);//删除最小堆内最小的元素
Bintree Create(Heap H);//创建哈夫曼树的函数
void Sum(Bintree B);//求最少花费的函数
int sum = 0;//记录结果
int main()
{
 int n;
 scanf("%d", &n);
 int i;
 Bintree B;
 Heap H;
 H = (Heap)malloc(sizeof(struct heap));
 H->d = (Bintree *)malloc((n+1) * sizeof(Bintree));
 for (i = 0; i <= n; i++)
 {
  H->d[i] = (Bintree)malloc(sizeof(struct node));
 }
 H->d[0]->data = -100;//岗哨
 H->d[0]->left = H->d[0]->right = NULL;
 H->size = 0;
 for (i = 1; i <= n; i++)
 {
  B = (Bintree)malloc(sizeof(struct node));
  scanf("%d", &B->data);
  B->left = B->right = NULL;
  H = Insert(H, B);
 }
 Bintree HF;
 HF = Create(H);
 Sum(HF);
 printf("%d", sum);
 return 0;
}
//向最小堆内插入元素
Heap Insert(Heap H, Bintree B)
{
 int i = ++H->size;
 while (B->data < H->d[i / 2]->data)
 {
  H->d[i] = H->d[i / 2];
  i = i / 2;
 }
 H->d[i] = B;
 return H;
}
//删除最小堆内最小的元素
Bintree Delete(Heap H)
{
 Bintree B;
 B = H->d[1];
 int i = H->size;
 int j = 2;
 while (j<i)
 {
  if (H->d[j]->data > H->d[j + 1]->data)
  {
   j = j + 1;
  }
  if (H->d[j]->data < H->d[i]->data)
  {
   H->d[j / 2] = H->d[j];
  }
  else { break; }
  j = 2 * j;
 }
 H->d[j/2] = H->d[i];
 H->size--;
 return B;
}
//创建哈夫曼树的函数
Bintree Create(Heap H)
{
 Bintree p1, p2,p;
 while (H->size != 1)//直至最小堆内仅剩一个元素,该元素内存的是哈夫曼树的根结点地址
 {
  p = (Bintree)malloc(sizeof(struct node));
  p1 = Delete(H);
  p2 = Delete(H);
  p->data = p1->data + p2->data;
  p->left = p1;
  p->right = p2;
  H = Insert(H, p);
 }
 return H->d[H->size];
}
//求最少花费的函数
//前序遍历,将所有非叶结点的data值求和,即为所求值
void Sum(Bintree B)
{
 if (B != NULL)
 {
  if (B->left != NULL && B->right != NULL) { sum = sum + B->data; }
  Sum(B->left);
  Sum(B->right);
 }
}
最大子列和问题是动态规划的一个经典问题,目标是找到给定数中连续子数的最大和。给定一个整数数 nums,我们需要找到一个连续子数(可以为空),使得这个子数的所有元素之和最大。 这个问题可以通过创建一个辅助数来解决,每个元素存储以当前位置结束的最长递增子序列的和。算法的主要思路是遍历整个数,对于每个元素,有三种情况: 1. 如果当前元素比前一个元素小,则它的最大子序和就是它本身,因为它需要从头开始构造一个新的子序列。 2. 如果当前元素大于或等于前一个元素,那么可以保留当前元素,或者从前一个元素开始加上当前元素,取两者中的较大值,作为新子序列的和。 3. 更新全局最大子列和,如果当前元素加入后的新子序列和更大。 以下是伪代码和关键步骤描述: ```python def maxSubArray(nums): # 初始化辅助数,first[i]表示以nums[i]结尾的最长递增子序列的起始索引 first = [0] * len(nums) # 初始化辅助数,max_sum[i]表示以nums[i]结尾的最长递增子序列的和 max_sum = [nums[0]] + [-float('inf')] * (len(nums) - 1) # 遍历数 for i in range(1, len(nums)): if nums[i] > nums[i - 1]: # 当前元素比前一个大,直接添加 max_sum[i] = nums[i] # 更新最长递增子序列的起始索引 first[i] = i else: # 否则,寻找左边界,使得nums[i] + 左边界之前的子序列和最大 left = first[i - 1] # 如果nums[left]加起来比nums[i]大,说明应该从left开始 max_sum[i] = max(max_sum[i], nums[i] + max_sum[left]) # 更新起始索引,选择能提供更大和的左边界 first[i] = left + 1 # 返回全局最大子列和 return max_sum[-1] # 示例 nums = [-2, 1, -3, 4, -1, 2, 1, -5, 4] print(maxSubArray(nums)) # 输出:6 (子数[4,-1,2,1]) ``` 相关问题-- 1. 除了动态规划,还有哪些其他解法可以解决最大子列和问题? 2. 这种方法为什么能在O(n)时间内解决问题? 3. 对于负数的数,这种解决方案还能正常工作吗?为什么?
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值