Sunday, August 13th
Partitions
Given an array of integers, determine whether or not the array can be divided into subarrays where each subarray meets any of the following criteria:
- The subarray is made up of two equal values, e.g. {2,2}
- The subarray is made up of three equal values, e.g. {2,2,2}
- The subarray is made up of three increasing values differing by 1, e.g. {3,4,5}
First we try a greedy approach to the problem, but quickly find examples where greedy won't work. Such as an array with values:
{ 2, 2, 2, 3, 4, 5, 5 }
Clearly choices made earlier in solving the problem will effect later choices, revealing overlapping subproblems, so for this we turn to dynamic programming.
We will begin by taking a top-down recursive approach since it's generally easier to implement
Let's define a function dp(i), which will return whether or not an array beginning at i can be properly partitioned
bool dp(int i) {
}
Since we are using dynamic programming, we define our memoization table to store intermediate solutions. Memoization is appropriate because consider the following array:
{ 2, 2, 2, 2, 2, 3, .... }
At the beginning of the array we can take either the first two 2's and the next three 2's, or we can take the first three 2's then the next two 2's. Multiple ways lead us to the same subproblem. So we use memoization to store previously computed subproblems.
vector<int> memo;
In the main function, we initialize variables and call dp()
bool validPartition(vector<int>& nums) {
memo=vector<int>(nums.size(), -1);
this->nums=nums;
this->n=nums.size();
return dp(0);
}
Now we turn attention to the dp function. We begin with base cases:
- If we reached the last element in the array, then it can't be partitioned since there are no other elements, so we set its memo value to 0
- If we reach the second to last element, we test if it shares the same value as the last element, and if so we set memo to 1, otherwise 0
- If we reached the third to last element, the we test for:
- If all the value are the same
- If the values are increasing and differ by 1
bool dp(int i) {
if(i==n-1) memo[n-1]=0;
else if(i==n-2) {
if(nums[n-2]==nums[n-1]) memo[n-2]=1;
else memo[n-2]=0;
}
else if(i==n-3) {
if(nums[n-3]==nums[n-2] && nums[n-2]==nums[n-1]){
memo[n-3]=1;
} else if(nums[n-3]+1==nums[n-2] && nums[n-2]+1==nums[n-1]){
memo[n-3]=1;
} else memo[n-3]= 0;
}
}
Following the base cases, we check to see if a given index in the array has already had results computed
if(memo[i]!=-1) return memo[i];
Then we set the recurrence relation. We test for each of the 3 subarray definitions, then recursively call the dp function for the remaining parts of the array
int res;
if(
(nums[i]==nums[i+1] && dp(i+2) ) ||
(nums[i]==nums[i+1] && nums[i+1]==nums[i+2] && dp(i+3) ) ||
(nums[i]+1==nums[i+1] && nums[i+1]+1==nums[i+2] && dp(i+3))
) {
res=1;
} else {
res=0;
}
Then set the result in the memo and return
bool dp(int i) {
....
return memo[i]=res;
}
Dynamic programming bread and butter
Comments
Post a Comment