类别:greedy
难度:medium
题目描述
首先,需要正确理解题目的意思,最开始的时候误解为是只要将所有的数字分成两组,然后每一组中的数字含有三个以上连续的数字,后来发现并不是。
题目的意思是:将数组划分为子数组,使得每个子数组中的数字都是连续无重复的并且每个子数组的长度需要大于等于三。判断是否能够进行这样的划分,可以的话返回true,否则返回false。
算法分析
在前面数字划分的基础上进行后面数字的划分。
需要保证数组划分以后不存在长度为1或者是长度为2的子数组。
每次对于一个新的遍历到的数字,优先划分给长度为2的数字。
举个例子:
1,2,3,4,4,5
首先是计算每一个数字(1,2,3,4,5)的数目,分别为:1,1,1,2,1
对1,2,3,4,5这些数字进行遍历:
- 到达1,以1结尾的长度为1的子数组的数目是1、以1结尾的长度为2的子数组的数目是0、以1结尾的总的子数组的数目的1 【1】
- 到达2,(2添加到第一步中长度为1的子数组中),以2结尾的长度为1的子数组的数目是0、以2结尾的长度为2的子数组的数目是1、以2结尾的总的子数组的数目的1 【1,2】
- 到达3,(3添加到第二步中长度为2的子数组中),以3结尾的长度为1的子数组的数目是0、以3结尾的长度为2的子数组的数目是1、以3结尾的总的子数组的数目的1 【1,2,3】
- 到达4,有两个4,这个时候,以4结尾的长度为1的子数组的数目是1、以4结尾的长度为2的子数组的数目是0、以3结尾的总的子数组的数目的2,【1,2,3,4】,【4】
- 到达5,【1,2,3,4】,【4,5】
- 可以得出规律,长度为1的以x结尾的子数组的数目为(x的数目-上一步中总的子数组的数目)
- 长度为2的以x结尾的子数组的数目为(上一步中以x-1结尾的子数组的数目)
- 总的以x结尾的子数组的数目为数字x的数目
因为最后到达第五步的时候已经结束了,但是还存在长度为2的分组,所以这个数组无法按要求进行划分。
代码实现:
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 把数组分成连贯的子数组(相邻两个数字之间相差1,并且不能够包含重复的数字)
// 并且子数组的长度必须大于等于3
bool check(vector<int>& nums, int begin, int end) {
int n = nums[end]-nums[begin]+1;
vector<int> count(n, 0);
int temp = nums[begin];
// 计算每个数字出现的次数
for (int i = begin; i <= end; ++i) {
count[nums[i] - temp]++;
}
vector<int> ones(n+1, 0), twos(n+1, 0), total(n+1,0);
for (int i = 0; i < n; ++i) {
if (count[i] < ones[i] + twos[i]) return false;
twos[i+1]=ones[i];
ones[i+1]=max(0, count[i]-total[i]);
total[i+1]=count[i];
}
return (ones[n] == 0) && (twos[n] == 0);
}
bool isPossible(vector<int>& nums) {
int n = nums.size();
int begin = 0;
for (int i = 1; i < n; ++i) {
if (nums[i] - nums[i - 1] > 1) {
if (!check(nums, begin, i-1)) {
return false;
}
begin = i;
}
}
return check(nums, begin, n - 1);
}
int main() {
int num, n;
cin >> n;
vector<int> nums;
for (int i = 0; i < n; ++i) {
cin >> num;
nums.push_back(num);
}
cout << isPossible(nums) << endl;
return 0;
}