【算法 | 实验7】以最小的步骤收集所有硬币(算法正确性还没想清楚)

发布于:2022-10-18 ⋅ 阅读:(534) ⋅ 点赞:(0)

题目

最小步骤收集硬币
有许多相邻排列的硬币堆。我们需要以最少的步骤收集所有这些硬币,在一个步骤中,我们可以收集一个水平线的硬币或垂直线的硬币,收集的硬币应该是连续的。
输入描述
输入第一行整数N表示硬币堆的数量
输入第二行有N个整数分表表示硬币堆的高度
输出描述
采集所有硬币需要最小步骤
样例输入
5
2 1 2 5 1
样例输出
4
解析
第一行表示有五堆硬币,
第二行表示第一堆有两枚硬币,第二堆一枚硬币,依此类推。
取硬币需要4步,如图所示,取硬币的步骤用不同颜色的线段表示。
在这里插入图片描述

问题分析和算法设计思路

基本思路:分治法

从下方删除水平线总是有益的,于是以左图方式对硬币对进行分治,将一个原问题划分为 3 个子问题,递归求解。但我们还有纵向收集硬币的方法,每次都取两种方案的最小值。

左图:从下方删除水平线
右图:一直纵向取完所有硬币

在这里插入图片描述

有过的一些疑问

1、既然已经对问题分治了,干嘛还要和纵向取完所有硬币的情况进行比较?
分别用分治和纵向取硬币的思路考虑,只有一列硬币的情况:
在这里插入图片描述

2、既然直接一直分治不是最优,还要和纵向取完所有硬币的情况比较,那分治和纵向取完就能保证最优了吗?会不会有其它更好的策略?比如“先纵取硬币最多的一列”会不会更好?
比较严谨的证明我也没想出来。
不过我们也许不必太纠结先取行还是先取列的问题,从最初题目的图可以看到,它只是用线条将这些硬币划分成了 4 部分,而线条之间是没有顺序的。也就是说一些看似不同的决策其实是等价的

3、在问题社区提问:以最小的步骤收集所有硬币,如何证明该分治算法的正确性?
问题链接:https://ask.csdn.net/questions/7810917
有个博主回答如下,可以参考

首先你要审题,下方是重力方向,所以下方的硬币数量必然比上方的多,硬币不能悬空
而取硬币一共就两种取法,要么取一横排,要么取一竖列
如果先按竖列取,会把最下面的横排截断,所以显然不是最好的方法
而先按横排取,不会截断其它行列
那么你不从长的开始取,难道从最短的开始取吗

算法实现

参考代码:https://www.imangodoc.com/83929.html

#include <bits/stdc++.h>
using namespace std;
  
int minStepsRecur(int height[], int l, int r, int h)
{
    if (l >= r)
        return 0;
  
    int m = l;
    for (int i = l; i < r; i++)
        if (height[i] < height[m])
            m = i;
  
    return min(r - l,
               minStepsRecur(height, l, m, height[m]) + 
               minStepsRecur(height, m + 1, r, height[m]) + 
               height[m] - h);
}
  
int minSteps(int height[], int N)
{
    return minStepsRecur(height, 0, N, 0);
}
  
int main()
{
	int N = 0;
	
	cin >> N;
    int *height = new int[N];
    for(int i = 0; i < N; i++)
    {
    	cin >> height[i];
	}
  
    cout << minSteps(height, N) << endl;
    return 0;
}

运行结果

在这里插入图片描述

算法分析

时间复杂度: O ( N ) \Omicron (N) O(N),N 为硬币堆的数量


本文含有隐藏内容,请 开通VIP 后查看

网站公告

今日签到

点亮在社区的每一天
去签到