NC630 牛牛摆放花

题目陈述

  • 大意:n朵花排成一圈,最小化相邻两朵花高度差的最大值,输出最大值。
  • 约定:将所求值成为丑陋值,即要求最小的丑陋值

算法一:暴力做法

算法思路

  • 因为已经知道了有n个数字,我们只需要生成这n个数字的所有排序
  • 然后依次计算每个序列的丑陋值,依次更新ans,求出最小的即可

代码实现

class Solution {
public:
    int arrangeFlowers(int n, vector<int>& a) {
        sort(a.begin(),a.end());//排序 
        int ans=1e9;//ans初值需要足够大,才能保证第一次就被更新掉 
		do{
			int now=abs(a[n-1]-a[0]);//因为是一个圆排列,首位也要 
			for(int i=0;i<n-1;i++){
				now=max(now,abs(a[i]-a[i+1]));//寻找最大差值 
			}
			ans=min(now,ans);//看看是否是更小的丑陋值 
		} while(next_permutation(a.begin(),a.end()));//继续生成下一个排序 
		return ans;
    }
};

复杂度分析

  • 时间复杂度,构造了所有排序,乘法原理易得为 O ( n ! ) O(n!) O(n!)
  • 空间复杂度,定义了ans,now,为 O ( 1 ) O(1) O(1)

算法二:构造法+双端队列

性质引入

  • 此处我们想用逆向思维,来倒推他的性质
  • 依旧先跟大家说一下结论,再来证明:如果我们将这个环,从最大值处断开(把最大值处变为链的左端),那么这条链必然是先递减再递增的

结论证明

  • 我们假设,序列中是否有可能有一处或者几处地方,出现”波浪形“(先增后减再增)?

  • 答案是不可能的,下面我给大家解释一下

  • 如果出现了这种情况,,避免用太抽象的语言,我给大家举个例子,如下图
    在这里插入图片描述

  • 如果我们将3,4交换,将”波浪形“调整为”单调型“
    在这里插入图片描述

  • 那么可以发现,将{3,4}看做一个整体,4在9旁边的差值明显比3在9旁边来的小,3在2旁边的差值也比4在2旁边的差值小

  • 对于最大值来说,我们要构造”单调型“,只能两边都是递减,这样整个序列的类型性质也就被确定了下来

  • 所以我们应该构造

^型

  • 这样的序列,(当然对于最小值构造”V“形也是一样的)
  • 故从从最大值处断开(把最大值处变为链的左端),那么这条链必然是先递减再递增的,得证

算法思路

  • 既然我们知道了性质,那么我们要构造,显然就不难了
  • (注意:上面我举得例子没按这个来构造,只是为了证明性质)

最大值处来构造

  • (注意:此处的数组a为排序过的)
  • 既然我们构造的是

^型

  • 那么我们显然应该围绕最大值来做,我们借助双端队列,先放入最大值a[n]
  • 要使最大值两端的数最小,不难看出应该左右各应该放入a[n-1],a[n-2]
  • 接下来我们又要让a[n-1],a[n-2]最小
  • 先考虑a[n-1],因为a[n-2]也被选取了,所以我们只能选择a[n-3]
  • 再考虑a[n-2],不难想到填写的是a[n-4]
  • 此处我们以及看出规律了,从大到小取数字,双端队列前后各放入一个数,这样得到的就是最优的答案

最小值处来构造

  • 其实跟上面大同小异
  • 构造为

V形

  • 从小到大取数字,双端队列前后各放入一个数,这样得到的也是最优的答案

代码实现

  • 以下为最大值处来构造
class Solution {
public:
    int arrangeFlowers(int n, vector<int>& a) {
        sort(a.begin(),a.end());//排序 
        deque<int> d;//双端队列 
        for(int i=n-1;i>=0;i--){//从小到大取数字 
        	d.push_back(a[i--]);//放入尾 
			if(i>=0)d.push_front(a[i]); //放入头       	
		}
		int ans=abs(d.front()-d.back());//圆排列还需要计算头尾的 差值 
		int head;
		for(int i=1;i<n;i++){
			head=d.front();
			d.pop_front();//双端队列不能直接访问,得先取出来 
			ans=max(ans,abs(head-d.front()));//计算相邻两个数的差值 
		}
		return ans;
    }
};

复杂度分析

  • 时间复杂度,排序数组为 O ( n log ⁡ n ) O(n \log n) O(nlogn),构造答案序列、求差值都为 O ( n ) O(n) O(n),总得为 O ( n log ⁡ n ) O(n \log n) O(nlogn)
  • 空间复杂度,定义了双端队列,为 O ( n ) O(n) O(n)

算法三:性质法

算法思路

  • 算法三本质上面跟算法二没有差别,只是算法二的改进
  • 我们可以从上述结论中发现,构造好的序列,除了最大值处,有个跟a[n-1]相邻的,其余的数的左右邻居,都是跟自己差值为2的(a[0],a[1]其实在头尾,最后会接起来)
  • 但是a[n]和a[n-1]、a[n-2]谁的差值大,显然是a[n-2],a[0]和a[1]、a[2]谁的差值大,显然是a[2]
  • 所以我们只需要通过排序后下标差值为2的数的差值即可求出答案

代码实现

class Solution {
public:
    int arrangeFlowers(int n, vector<int>& a) {
    	if(n==2){//特判两个数的情况 
    		return abs(a[1]-a[0]);
		}
        sort(a.begin(),a.end());//排序 
		int ans=0;
		for(int i=2;i<n;i+=2)ans=max(ans,a[i]-a[i-2]);//下标为偶数的 
		for(int i=3;i<n;i+=2)ans=max(ans,a[i]-a[i-2]);//下标为奇数的 
		return ans;
    }
};

复杂度分析

  • 时间复杂度,排序数组为 O ( n log ⁡ n ) O(n \log n) O(nlogn),求差值为 O ( n ) O(n) O(n),总得为 O ( n log ⁡ n ) O(n \log n) O(nlogn)
  • 空间复杂度,定义了ans,为 O ( 1 ) O(1) O(1)
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值