给定一个数组arr []和一个整数K ,任务是将该数组划分为K个非空组,其中每个组都是给定数组的子数组,而数组的每个元素仅是一个组的一部分。给定组中的所有元素必须具有相同的值。您可以多次执行以下操作:
从数组中选择一个元素,然后将其值更改为任何值。打印分区阵列所需的最少此类操作数。
例子:
Input: arr[] = {3, 1, 3, 3, 2, 1, 8, 5}, K = 3
Output: 3
The array can be partitioned in {3, 3, 3, 3}, {2, 2} and {8, 8}
by changing the 2nd element to 3, the 6th
element to 2 and the last element to 8.
Input:arr[] = {3, 3, 9, 10}, K = 3
Output: 0
Divide the array in groups {3, 3}, {9} and {10}
without performing any operations.
观察结果:
- 如果K = 1,则该组本身就是完整的数组。为了最大程度地减少所需的操作数量,最直观的操作是更改阵列的所有元素,并使它们等于阵列的模式(频率最高的元素)。
- 为K组,阵列的最后一个元素永远属于第K组,而第1个元件将属于第1组。
- 如果正确找到第K个组,则问题将减少到使用最少的操作将剩余的阵列划分为K – 1个组。
方法:可以使用动态编程解决此问题。
- 令DP(i,j)表示将array [1..i]划分为j个组所需的最小操作。
- 现在,任务是找到DP(N,K) ,这是将array [1..N]划分为K个组所需的最少操作。
- j = 1的基本情况DP(i,j)可以很容易地回答。由于完整的数组array [1..i]仅需要划分为一个组。从观察,发现在阵列[1..i]的模式,并且改变阵列的所有元素[1..i]到模式。如果该模式发生了x次,则必须更改i – x个元素,即i – x个操作。
- 此后,第K个组在最后一个元素处结束。但是,它可能从各种可能的位置开始。假设在某些索引它然后阵列[it..N]需要第K组开始被划分成一个组和阵列[1 ..(它- 1)]需要被划分成K – 1个基团。将数组[1 ..(it – 1)]划分为K – 1组的成本为DP(it – 1,K – 1) ,可以计算单个组中的数组[it..N]的成本使用该模式及其频率观察。
- 为了找到在[it..i]范围内最常出现的元素的频率,我们可以使用哈希图和整数变量。整数变量表示当前的最高频率。该地图存储了到目前为止所看到的所有元素及其频率。每当在地图上看到某个元素时,它的频率就会增加,如果现在该元素的频率高于当前的最高频率,我们会将当前的最高频率更新为刚才看到的元素的频率。请参考此方法。
- 因此DP(I,J)为最小的DP(它- 1,J – 1)+分隔阵列的成本[it..i]到1个基团为它的所有可能值。
下面是上述方法的实现:
C++
// C++ implementation of the approach
#include
using namespace std;
// Function to return the minimum number
// of operations needed to partition
// the array in k contiguous groups
// such that all elements of a
// given group are identical
int getMinimumOps(vector ar, int k)
{
// n is the size of the array
int n = ar.size();
// dp(i, j) represents the minimum cost for
// partitioning the array[0..i] into j groups
int dp[n][k + 1];
// Base case, cost is 0 for parititoning the
// array[0..0] into 1 group
dp[0][1] = 0;
// Fill dp(i, j) and the answer will
// be stored at dp(n-1, k)
for (int i = 1; i < n; i++) {
// The maximum groups that the segment 0..i can
// be divided in is represented by maxGroups
int maxGroups = min(k, i + 1);
for (int j = 1; j <= maxGroups; j++) {
// Initialize dp(i, j) to infinity
dp[i][j] = INT_MAX;
// Divide segment 0..i in 1 group
if (j == 1) {
// map and freqOfMode are together used to
// keep track of the frequency of the most
// occurring element in [0..i]
unordered_map freq;
int freqOfMode = 0;
for (int it = 0; it <= i; it++) {
freq[ar[it]]++;
int newElementFreq = freq[ar[it]];
if (newElementFreq > freqOfMode)
freqOfMode = newElementFreq;
}
// Change all the elements in the range
// 0..i to the most frequent element
// in this range
dp[i][1] = (i + 1) - freqOfMode;
}
else {
unordered_map freq;
int freqOfMode = 0;
// If the jth group is the segment from
// it..i, we change all the elements in this
// range to this range's most occurring element
for (int it = i; it >= j - 1; it--) {
freq[ar[it]]++;
int newElementFreq = freq[ar[it]];
if (newElementFreq > freqOfMode)
freqOfMode = newElementFreq;
// Number of elements we need to change
// in the jth group i.e. the range it..i
int elementsToChange = i - it + 1;
elementsToChange -= freqOfMode;
// For all the possible sizes of the jth
// group that end at the ith element
// we pick the size that gives us the minimum
// cost for dp(i, j)
// elementsToChange is the cost of making
// all the elements in the jth group identical
// and we make use of dp(it - 1, j - 1) to
// find the overall minimal cost
dp[i][j] = min(dp[it - 1][j - 1]
+ elementsToChange,
dp[i][j]);
}
}
}
}
// Return the minimum cost for
// partitioning array[0..n-1]
// into k groups which is
// stored at dp(n-1, k)
return dp[n - 1][k];
}
// Driver code
int main()
{
int k = 3;
vector ar = { 3, 1, 3, 3, 2, 1, 8, 5 };
cout << getMinimumOps(ar, k);
return 0;
}
Java
// Java implementation of above approach
class GFG
{
// Function to return the minimum number
// of operations needed to partition
// the array in k contiguous groups
// such that all elements of a
// given group are identical
static int getMinimumOps(int ar[], int k)
{
// n is the size of the array
int n = ar.length;
// dp(i, j) represents the minimum cost for
// partitioning the array[0..i] into j groups
int dp[][] = new int[n][k + 1];
// Base case, cost is 0 for parititoning the
// array[0..0] into 1 group
dp[0][1] = 0;
// Fill dp(i, j) and the answer will
// be stored at dp(n-1, k)
for (int i = 1; i < n; i++)
{
// The maximum groups that the segment 0..i can
// be divided in is represented by maxGroups
int maxGroups = Math.min(k, i + 1);
for (int j = 1; j <= maxGroups; j++)
{
// Initialize dp(i, j) to infinity
dp[i][j] = Integer.MAX_VALUE;
// Divide segment 0..i in 1 group
if (j == 1)
{
// map and freqOfMode are together used to
// keep track of the frequency of the most
// occurring element in [0..i]
int freq[] = new int[100000];
int freqOfMode = 0;
for (int it = 0; it <= i; it++)
{
freq[ar[it]]++;
int newElementFreq = freq[ar[it]];
if (newElementFreq > freqOfMode)
freqOfMode = newElementFreq;
}
// Change all the elements in the range
// 0..i to the most frequent element
// in this range
dp[i][1] = (i + 1) - freqOfMode;
}
else
{
int freq[] = new int[100000];
int freqOfMode = 0;
// If the jth group is the segment from
// it..i, we change all the elements in this
// range to this range's most occurring element
for (int it = i; it >= j - 1; it--)
{
freq[ar[it]]++;
int newElementFreq = freq[ar[it]];
if (newElementFreq > freqOfMode)
freqOfMode = newElementFreq;
// Number of elements we need to change
// in the jth group i.e. the range it..i
int elementsToChange = i - it + 1;
elementsToChange -= freqOfMode;
// For all the possible sizes of the jth
// group that end at the ith element
// we pick the size that gives us the minimum
// cost for dp(i, j)
// elementsToChange is the cost of making
// all the elements in the jth group identical
// and we make use of dp(it - 1, j - 1) to
// find the overall minimal cost
dp[i][j] = Math.min(dp[it - 1][j - 1] +
elementsToChange, dp[i][j]);
}
}
}
}
// Return the minimum cost for
// partitioning array[0..n-1]
// into k groups which is
// stored at dp(n-1, k)
return dp[n - 1][k];
}
// Driver code
public static void main(String args[])
{
int k = 3;
int ar[] = { 3, 1, 3, 3, 2, 1, 8, 5 };
System.out.println(getMinimumOps(ar, k));
}
}
// This code is contributed by Arnab Kundu
Python3
# Python3 implementation of the approach
# Function to return the minimum number
# of operations needed to partition
# the array in k contiguous groups
# such that all elements of a
# given group are identical
def getMinimumOps(ar, k):
# n is the size of the array
n = len(ar)
# dp(i, j) represents the minimum cost for
# partitioning the array[0..i] into j groups
dp = [[ 0 for i in range(k + 1)]
for i in range(n)]
# Base case, cost is 0 for parititoning the
# array[0..0] into 1 group
dp[0][1] = 0
# Fill dp(i, j) and the answer will
# be stored at dp(n-1, k)
for i in range(1, n):
# The maximum groups that the segment 0..i can
# be divided in is represented by maxGroups
maxGroups = min(k, i + 1)
for j in range(1, maxGroups + 1):
# Initialize dp(i, j) to infinity
dp[i][j] = 10**9
# Divide segment 0..i in 1 group
if (j == 1):
# map and freqOfMode are together used to
# keep track of the frequency of the most
# occurring element in [0..i]
freq1 = dict()
freqOfMode = 0
for it in range(0, i + 1):
freq1[ar[it]] = freq1.get(ar[it], 0) + 1
newElementFreq = freq1[ar[it]]
if (newElementFreq > freqOfMode):
freqOfMode = newElementFreq
# Change all the elements in the range
# 0..i to the most frequent element
# in this range
dp[i][1] = (i + 1) - freqOfMode
else:
freq = dict()
freqOfMode = 0
# If the jth group is the segment from
# it..i, we change all the elements in this
# range to this range's most occurring element
for it in range(i, j - 2, -1):
#print(i,j,it)
freq[ar[it]] = freq.get(ar[it], 0) + 1
newElementFreq = freq[ar[it]]
if (newElementFreq > freqOfMode):
freqOfMode = newElementFreq
# Number of elements we need to change
# in the jth group i.e. the range it..i
elementsToChange = i - it + 1
elementsToChange -= freqOfMode
# For all the possible sizes of the jth
# group that end at the ith element
# we pick the size that gives us the minimum
# cost for dp(i, j)
# elementsToChange is the cost of making
# all the elements in the jth group identical
# and we make use of dp(it - 1, j - 1) to
# find the overall minimal cost
dp[i][j] = min(dp[it - 1][j - 1] +
elementsToChange, dp[i][j])
# Return the minimum cost for
# partitioning array[0..n-1]
# into k groups which is
# stored at dp(n-1, k)
return dp[n - 1][k]
# Driver code
k = 3
ar =[3, 1, 3, 3, 2, 1, 8, 5]
print(getMinimumOps(ar, k))
# This code is contributed by Mohit Kumar
C#
// C# implementation of above approach
using System;
class GFG
{
// Function to return the minimum number
// of operations needed to partition
// the array in k contiguous groups
// such that all elements of a
// given group are identical
static int getMinimumOps(int []ar, int k)
{
// n is the size of the array
int n = ar.Length;
// dp(i, j) represents the minimum cost for
// partitioning the array[0..i] into j groups
int [,]dp = new int[n, k + 1];
// Base case, cost is 0 for parititoning the
// array[0..0] into 1 group
dp[0, 1] = 0;
// Fill dp(i, j) and the answer will
// be stored at dp(n-1, k)
for (int i = 1; i < n; i++)
{
// The maximum groups that the segment 0..i can
// be divided in is represented by maxGroups
int maxGroups = Math.Min(k, i + 1);
for (int j = 1; j <= maxGroups; j++)
{
// Initialize dp(i, j) to infinity
dp[i, j] = int.MaxValue;
// Divide segment 0..i in 1 group
if (j == 1)
{
// map and freqOfMode are together used to
// keep track of the frequency of the most
// occurring element in [0..i]
int []freq = new int[100000];
int freqOfMode = 0;
for (int it = 0; it <= i; it++)
{
freq[ar[it]]++;
int newElementFreq = freq[ar[it]];
if (newElementFreq > freqOfMode)
freqOfMode = newElementFreq;
}
// Change all the elements in the range
// 0..i to the most frequent element
// in this range
dp[i, 1] = (i + 1) - freqOfMode;
}
else
{
int []freq = new int[100000];
int freqOfMode = 0;
// If the jth group is the segment from
// it..i, we change all the elements in this
// range to this range's most occurring element
for (int it = i; it >= j - 1; it--)
{
freq[ar[it]]++;
int newElementFreq = freq[ar[it]];
if (newElementFreq > freqOfMode)
freqOfMode = newElementFreq;
// Number of elements we need to change
// in the jth group i.e. the range it..i
int elementsToChange = i - it + 1;
elementsToChange -= freqOfMode;
// For all the possible sizes of the jth
// group that end at the ith element
// we pick the size that gives us the minimum
// cost for dp(i, j)
// elementsToChange is the cost of making
// all the elements in the jth group identical
// and we make use of dp(it - 1, j - 1) to
// find the overall minimal cost
dp[i, j] = Math.Min(dp[it - 1, j - 1] +
elementsToChange, dp[i, j]);
}
}
}
}
// Return the minimum cost for
// partitioning array[0..n-1]
// into k groups which is
// stored at dp(n-1, k)
return dp[n - 1, k];
}
// Driver code
public static void Main(String []args)
{
int k = 3;
int []ar = {3, 1, 3, 3, 2, 1, 8, 5};
Console.WriteLine(getMinimumOps(ar, k));
}
}
// This code is contributed by 29AjayKumar
输出:
3
时间复杂度: O(N * N * K)其中N是数组的大小,K是数组应划分为的组数。
空间复杂度: O(N * K)