构造数组arr,满足:对于任意 i < k < j ,arr[i] + arr[j] != 2 * arr[k]

题目描述:

  1. 构造长度为N的数组arr,此数组需要满足: 对于任意 i < k < j ,arr[i] + arr[j] != 2 * arr[k] 。

这里有个前提:
pic1
对原数组做奇变换和偶变换,也达标,可以证明的。见下面:
pic2
其实加1、加2、减100等等这种变换也能够达标,那为啥要奇偶变换呢?

因为好做啊,想象我们要扩大数组,如果我们前面拿一个达标的奇数数组,后面拿一个达标的偶数数组, (一个odd + 一个even 一定不会等于 2 * 一个num )的。所以,拼接起来的大数组一定是达标的。

在这里插入图片描述
代码:

/** 题目描述: 
*		1. 构造长度为N的数组arr,此数组需要满足: 
*		对于任意 i < k < j ,arr[i] + arr[j] != 2 * arr[k] 。 
*/ 
#include <iostream>  
#include <vector> 

/** 递归构造达标的数组。 
*	@param n:要构造的数组的长度
*   @return 一个vector 
*/
std::vector<int> constructArray(int n) {
	if (n <= 1)                        //base case
		return std::vector<int>(1, 1);
	
	int halfSize = (n + 1) >> 1;
	std::vector<int> base = constructArray(halfSize);  // 递归调用
	std::vector<int> res(n, 0);
	
	int index = 0;
	for (; index < halfSize; ++index) {
		res[index] = 2 * base[index] + 1;
	}
	for (int i = 0; index < n; ++index, ++i) {
		res[index] = 2 * base[i];
	}
	
	return res;      // 返回局部对象的拷贝,这里会发生调用原局部对象的析构,
					 // 以及需要调用生成临时对象的拷贝构造。
					 // 函数的返回不能是引用&。
}

/** 检验函数,暴力做法,判断arr是不是达标的。 */ 
bool isValid(const std::vector<int>& arr) {   // test()函数时间主要花在这里 O(N^3)
	int size = arr.size();
	for (int i = 0; i < size - 2; ++i) {
		for (int k = i + 1; k < size - 1; ++k) {
			for (int j = k + 1; j < size; ++j) {
				if (arr[i] + arr[j] == 2 * arr[k]) {
					return false;
				}
 			}
		}
	}
	return true;
}

/** 对数器测试函数 */
void test() {
	std::cout << "test begin.\n";
	for (int i = 1; i < 500; ++i) { 
		std::vector<int> arr = constructArray(i);
		if (isValid(arr)) {
			std::cout << i << ":Success!\n";
		}
		else {
			std::cout << i << ":Oops!\n";
		}
	}
	std::cout <<"test end.\n\n";
	
	// 单独两个test case
	std::cout << isValid(constructArray(1000)) << "\n"; 
	std::cout << isValid(constructArray(500)) << "\n"; 
}

以上测试肯定是能通过的。

constructArray() 函数的时间复杂度为 O(N),

递推公式为:T(N) = T(N/2) + O(N)。可以手推一下,或者直接根据Master公式就可以了。

这种重业务的题目,每次肯定都是不一样的,积累吧!或者智力很顶的人问题也不大。

纸上得来终觉浅,绝知此事要躬行。

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值