给定一个数组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)表示将数组 [1..i]划分为j 个组所需的最少操作。
- 现在,任务是找到DP(N, K) ,这是将数组 [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个基团。将array[1..(it – 1)]划分为K – 1 个组的代价是DP(it – 1, K – 1)并且可以计算出将array[it..N]划分为一个组的代价使用模式和它的频率观察。
- 要找到范围[it..i] 中出现次数最多的元素的频率,我们可以使用哈希图和整数变量。整数变量表示当前最高频率。该地图存储了迄今为止看到的所有元素及其频率。每当看到一个元素时,它在地图中的频率就会增加,如果现在这个元素的频率高于当前的最高频率,我们将当前的最高频率更新为刚刚看到的元素的频率。请参阅此方法。
- 因此, DP(i, j)是DP(it – 1, j – 1) + 将 array[it..i] 划分为 1 个组的成本的最小值,以获取it 的所有可能值。
下面是上述方法的实现:
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
Javascript
输出:
3
时间复杂度: O(N * N * K),其中 N 是数组的大小,K 是数组应划分为的组数。
空间复杂度: O(N * K)