给定一个数字数组和一个常数 k,使用以下删除元素的规则来最小化数组的大小。
- 一次可以移除正好三个元素。
- 移除的三个元素必须在数组中相邻,即arr[i]、arr[i+1]、arr[i+2]。并且第二个元素必须比第一个元素大 k,第三个元素必须比第二个元素大 k,即 arr[i+1] – arr[i] = k 和 arr[i+2]-arr[i+1] =克。
例子:
Input: arr[] = {2, 3, 4, 5, 6, 4}, k = 1
Output: 0
We can actually remove all elements.
First remove 4, 5, 6 => We get {2, 3, 4}
Now remove 2, 3, 4 => We get empty array {}
Input: arr[] = {2, 3, 4, 7, 6, 4}, k = 1
Output: 3
We can only remove 2 3 4
来源:https://code.google.com/codejam/contest/4214486/dashboard#s=p2
我们强烈建议您将浏览器最小化,然后自己先尝试一下。
对于每个元素 arr[i] 有两种可能性。
1) 元素没有被移除。
2) OR 元素被移除(如果它遵循移除规则)。当一个元素被移除时,又有两种可能性。
…..a) 可以直接去掉,即初始的 arr[i+1] 是 arr[i]+k,而 arr[i+2] 是 arr[i] + 2*k。
…..b) 存在 x 和 y 使得 arr[x] – arr[i] = k,arr[y] – arr[x] = k,以及子数组“arr[i+1…x-1]” & “arr[x+1…y-1]”可以完全去掉。
下面是基于上述思想的递归算法。
// Returns size of minimum possible size of arr[low..high]
// after removing elements according to given rules
findMinSize(arr[], low, high, k)
// If there are less than 3 elements in arr[low..high]
1) If high-low+1 < 3, return high-low+1
// Consider the case when 'arr[low]' is not considered as
// part of any triplet to be removed. Initialize result
// using this case
2) result = 1 + findMinSize(arr, low+1, high)
// Case when 'arr[low]' is part of some triplet and removed
// Try all possible triplets that have arr[low]
3) For all i from low+1 to high
For all j from i+1 to high
Update result if all of the following conditions are met
a) arr[i] - arr[low] = k
b) arr[j] - arr[i] = k
c) findMinSize(arr, low+1, i-1, k) returns 0
d) findMinSize(arr, i+1, j-1, k) also returns 0
e) Result calculated for this triplet (low, i, j)
is smaller than existing result.
4) Return result
上述解决方案的时间复杂度是指数级的。如果我们绘制完整的递归树,我们可以观察到许多子问题一次又一次地被解决。由于再次调用相同的子问题,因此该问题具有重叠子问题的属性。与其他典型的动态规划 (DP) 问题一样,通过构造一个临时数组 dp[][] 来存储子问题的结果,可以避免对相同子问题的重新计算。以下是基于动态规划的解决方案
下面是上述想法的实现。该实现是基于记忆的,即它是递归的,并使用查找表 dp[][] 来检查子问题是否已经解决。
C++
// C++ program to find size of minimum possible array after
// removing elements according to given rules
#include
using namespace std;
#define MAX 1000
// dp[i][j] denotes the minimum number of elements left in
// the subarray arr[i..j].
int dp[MAX][MAX];
int minSizeRec(int arr[], int low, int high, int k)
{
// If already evaluated
if (dp[low][high] != -1)
return dp[low][high];
// If size of array is less than 3
if ( (high-low + 1) < 3)
return high-low +1;
// Initialize result as the case when first element is
// separated (not removed using given rules)
int res = 1 + minSizeRec(arr, low+1, high, k);
// Now consider all cases when first element forms a triplet
// and removed. Check for all possible triplets (low, i, j)
for (int i = low+1; i<=high-1; i++)
{
for (int j = i+1; j <= high; j++ )
{
// Check if this triplet follows the given rules of
// removal. And elements between 'low' and 'i' , and
// between 'i' and 'j' can be recursively removed.
if (arr[i] == (arr[low] + k) &&
arr[j] == (arr[low] + 2*k) &&
minSizeRec(arr, low+1, i-1, k) == 0 &&
minSizeRec(arr, i+1, j-1, k) == 0)
{
res = min(res, minSizeRec(arr, j+1, high, k));
}
}
}
// Insert value in table and return result
return (dp[low][high] = res);
}
// This function mainlu initializes dp table and calls
// recursive function minSizeRec
int minSize(int arr[], int n, int k)
{
memset(dp, -1, sizeof(dp));
return minSizeRec(arr, 0, n-1, k);
}
// Driver prrogram to test above function
int main()
{
int arr[] = {2, 3, 4, 5, 6, 4};
int n = sizeof(arr)/sizeof(arr[0]);
int k = 1;
cout << minSize(arr, n, k) << endl;
return 0;
}
Java
// Java program to find size of
// minimum possible array after
// removing elements according
// to given rules
class GFG
{
static int MAX = 1000;
// dp[i][j] denotes the minimum
// number of elements left in
// the subarray arr[i..j].
static int dp[][] = new int[MAX][MAX];
static int minSizeRec(int arr[], int low,
int high, int k)
{
// If already evaluated
if (dp[low][high] != -1)
{
return dp[low][high];
}
// If size of array is less than 3
if ((high - low + 1) < 3)
{
return high - low + 1;
}
// Initialize result as the
// case when first element is
// separated (not removed
// using given rules)
int res = 1 + minSizeRec(arr,
low + 1, high, k);
// Now consider all cases when
// first element forms a triplet
// and removed. Check for all
// possible triplets (low, i, j)
for (int i = low + 1; i <= high - 1; i++)
{
for (int j = i + 1; j <= high; j++)
{
// Check if this triplet
// follows the given rules of
// removal. And elements
// between 'low' and 'i' , and
// between 'i' and 'j' can
// be recursively removed.
if (arr[i] == (arr[low] + k) &&
arr[j] == (arr[low] + 2 * k) &&
minSizeRec(arr, low + 1, i - 1, k) == 0 &&
minSizeRec(arr, i + 1, j - 1, k) == 0)
{
res = Math.min(res, minSizeRec(arr, j + 1, high, k));
}
}
}
// Insert value in table and return result
return (dp[low][high] = res);
}
// This function mainlu initializes
// dp table and calls recursive
// function minSizeRec
static int minSize(int arr[], int n, int k)
{
for (int i = 0; i < MAX; i++)
{
for (int j = 0; j < MAX; j++)
{
dp[i][j] = -1;
}
}
return minSizeRec(arr, 0, n - 1, k);
}
// Driver code
public static void main(String[] args)
{
int arr[] = {2, 3, 4, 5, 6, 4};
int n = arr.length;
int k = 1;
System.out.println(minSize(arr, n, k));
}
}
// This code is contributed by 29AjayKumar
Python3
# Python3 program to find size of
# minimum possible array after
# removing elements according to given rules
MAX=1000
dp=[[-1 for i in range(MAX)] for i in range(MAX)]
# dp[i][j] denotes the minimum number of elements left in
# the subarray arr[i..j].
def minSizeRec(arr,low,high,k):
# If already evaluated
if dp[low][high] != -1:
return dp[low][high]
# If size of array is less than 3
if (high-low + 1) < 3:
return (high-low + 1)
# Initialize result as the case when first element is
# separated (not removed using given rules)
res = 1 + minSizeRec(arr, low+1, high, k)
# Now consider all cases when
# first element forms a triplet
# and removed. Check for all possible
# triplets (low, i, j)
for i in range(low+1,high):
for j in range(i+1,high+1):
# Check if this triplet follows the given rules of
# removal. And elements between 'low' and 'i' , and
# between 'i' and 'j' can be recursively removed.
if (arr[i]==(arr[low]+k) and arr[j] == (arr[low] + 2*k) and
minSizeRec(arr, low+1, i-1, k) == 0 and
minSizeRec(arr, i+1, j-1, k) == 0):
res=min(res,minSizeRec(arr,j+1,high,k) )
# Insert value in table and return result
dp[low][high] = res
return res
# This function mainly initializes dp table and calls
# recursive function minSizeRec
def minSize(arr,n,k):
dp=[[-1 for i in range(MAX)] for i in range(MAX)]
return minSizeRec(arr, 0, n-1, k)
# Driver program to test above function
if __name__=='__main__':
arr=[2, 3, 4, 5, 6, 4]
n=len(arr)
k=1
print(minSize(arr,n,k))
# this code is contributed by sahilshelangia
C#
// C# program to find size of
// minimum possible array after
// removing elements according
// to given rules
using System;
class GFG
{
static int MAX = 1000;
// dp[i,j] denotes the minimum
// number of elements left in
// the subarray arr[i..j].
static int [,]dp = new int[MAX, MAX];
static int minSizeRec(int []arr, int low,
int high, int k)
{
// If already evaluated
if (dp[low, high] != -1)
{
return dp[low, high];
}
// If size of array is less than 3
if ((high - low + 1) < 3)
{
return high - low + 1;
}
// Initialize result as the
// case when first element is
// separated (not removed
// using given rules)
int res = 1 + minSizeRec(arr,
low + 1, high, k);
// Now consider all cases when
// first element forms a triplet
// and removed. Check for all
// possible triplets (low, i, j)
for (int i = low + 1; i <= high - 1; i++)
{
for (int j = i + 1; j <= high; j++)
{
// Check if this triplet
// follows the given rules of
// removal. And elements
// between 'low' and 'i' , and
// between 'i' and 'j' can
// be recursively removed.
if (arr[i] == (arr[low] + k) &&
arr[j] == (arr[low] + 2 * k) &&
minSizeRec(arr, low + 1, i - 1, k) == 0 &&
minSizeRec(arr, i + 1, j - 1, k) == 0)
{
res = Math.Min(res, minSizeRec(arr, j + 1, high, k));
}
}
}
// Insert value in table and return result
return (dp[low, high] = res);
}
// This function mainlu initializes
// dp table and calls recursive
// function minSizeRec
static int minSize(int []arr, int n, int k)
{
for (int i = 0; i < MAX; i++)
{
for (int j = 0; j < MAX; j++)
{
dp[i, j] = -1;
}
}
return minSizeRec(arr, 0, n - 1, k);
}
// Driver code
public static void Main(String[] args)
{
int []arr = {2, 3, 4, 5, 6, 4};
int n = arr.Length;
int k = 1;
Console.WriteLine(minSize(arr, n, k));
}
}
// This code contributed by Rajput-Ji
Javascript
输出:
0
如果您希望与专家一起参加现场课程,请参阅DSA 现场工作专业课程和学生竞争性编程现场课程。