📌  相关文章
📜  最小化将阵列划分为K个组的成本

📅  最后修改于: 2021-04-29 07:42:51             🧑  作者: Mango

给定一个数组arr []和一个整数K ,任务是将该数组划分为K个非空组,其中每个组都是给定数组的子数组,而数组的每个元素仅是一个组的一部分。给定组中的所有元素必须具有相同的值。您可以多次执行以下操作:
从数组中选择一个元素,然后将其值更改为任何值。打印分区阵列所需的最少此类操作数。

例子:

观察结果:

  • 如果K = 1,则该组本身就是完整的数组。为了最大程度地减少所需的操作数量,最直观的操作是更改阵列的所有元素,并使它们等于阵列的模式(频率最高的元素)。
  • K组,阵列的最后一个元素永远属于K组,而第1元件将属于第1组。
  • 如果正确找到K组,则问题将减少到使用最少的操作将剩余的阵列划分为K – 1个组。

方法:可以使用动态编程解决此问题。

  1. DP(i,j)表示将array [1..i]划分为j个组所需的最小操作。
  2. 现在,任务是找到DP(N,K) ,这是将array [1..N]划分为K个组所需的最少操作。
  3. j = 1的基本情况DP(i,j)可以很容易地回答。由于完整的数组array [1..i]仅需要划分为一个组。从观察,发现在阵列[1..i]的模式,并且改变阵列的所有元素[1..i]到模式。如果该模式发生了x次,则必须更改i – x个元素,即i – x个操作。
  4. 此后,第K组在最后一个元素处结束。但是,它可能从各种可能的位置开始。假设在某些索引然后阵列[it..N]需要K组开始被划分成一个组和阵列[1 ..(它- 1)]需要被划分成K – 1个基团。将数组[1 ..(it – 1)]划分为K – 1组的成本为DP(it – 1,K – 1) ,可以计算单个组中的数组[it..N]的成本使用该模式及其频率观察。
  5. 为了找到在[it..i]范围内最常出现的元素的频率,我们可以使用哈希图和整数变量。整数变量表示当前的最高频率。该地图存储了到目前为止所看到的所有元素及其频率。每当在地图上看到某个元素时,它的频率就会增加,如果现在该元素的频率高于当前的最高频率,我们会将当前的最高频率更新为刚才看到的元素的频率。请参考此方法。
  6. 因此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)