分割数组 - 力扣中等
给定一个数组 nums
,将其划分为两个连续子数组 left
和 right
, 使得:
left
中的每个元素都小于或等于 right
中的每个元素。
left
和 right
都是非空的。
left
的长度要尽可能小。
在完成这样的分组后返回 left
的 长度 。
用例可以保证存在这样的划分方法。
示例 1:
输入:nums = [5,0,3,8,6]
输出:3
解释:left = [5,0,3],right = [8,6]
示例 2:
输入:nums = [1,1,1,0,6,12]
输出:4
解释:left = [1,1,1,0],right = [6,12]
提示:
2
<
=
n
u
m
s
.
l
e
n
g
t
h
<
=
1
0
5
0
<
=
n
u
m
s
[
i
]
<
=
1
0
6
可以保证至少有一种方法能够按题目所描述的那样对
n
u
m
s
进行划分。
2 <= nums.length <= 10^5 \\ 0 <= nums[i] <= 10^6 \\ 可以保证至少有一种方法能够按题目所描述的那样对 nums 进行划分。
2<=nums.length<=1050<=nums[i]<=106可以保证至少有一种方法能够按题目所描述的那样对nums进行划分。
解题思路
假设从
i
i
i的位置进行分割 ,
i
l
e
f
t
<
=
i
,
i
r
i
g
h
t
>
i
\begin{align} & i_{left}<=i, \\ & i_{right}>i \end{align}
ileft<=i,iright>i
那么我们只需要知道知道此时左区间最大的数字
l
m
a
x
l_{max}
lmax和右区间最小的数字
r
m
i
n
r_{min}
rmin即可,如果
l
m
a
x
≤
r
m
i
n
l_{max}\leq r_{min}
lmax≤rmin,那么这个分割是可以的。
因此,我们可以开一个数组
d
p
[
N
]
[
2
]
,
0
<
i
<
N
dp[N][2], 0<i<N
dp[N][2],0<i<N,
i
i
i代表分割的位置,
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]保存分割位置为
i
i
i时左区间对应最大值,
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1]保存分割位置为
i
i
i时右区间对应最小值。
但是题目要求左区间越短越好,那么我们可以先倒序遍历一遍数组,将索引
i
i
i 作为分割位置时右区间对应的最小值
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1]都存下来。
再正序遍历一遍,观察索引
i
i
i 作为分割位置时左区间对应的最大值
d
p
[
i
]
[
0
]
dp[i][0]
dp[i][0]是不是小于等于
d
p
[
i
]
[
1
]
dp[i][1]
dp[i][1],如果满足条件,就返回
i
+
1
i+1
i+1即可。
解题代码
#include<iostream>
#include<vector>
using namespace std;
const int MAX_LEN = 1e5+10;
class Solution {
public:
int partitionDisjoint(vector<int>& nums) {
int dp[nums.size()+5][2];
for(int i=0;i<nums.size();i++){
dp[i][0]=-1;
dp[i][1]=1e6+10;
}
for(int i=nums.size()-2;i>=0;i--){
dp[i][1]=min(nums[i+1],dp[i+1][1]);
}
for(int i=0;i<nums.size();i++){
if(i==0){
dp[i][0]=nums[i];
}
else{
dp[i][0]=max(nums[i],dp[i-1][0]);
}
if (dp[i][0]<=dp[i][1]){
return i+1;
}
}
return -1;
}
};
int main(){
vector<int>nums = {5,0,3,8,6};
Solution so = Solution();
cout<<so.partitionDisjoint(nums)<<endl;
return 0;
}