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

📅  最后修改于: 2021-09-17 07:17:30             🧑  作者: Mango

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

观察:

  • 如果K = 1,则该组就是完整的数组本身。为了尽量减少所需的操作次数,最直观的做法是改变数组的所有元素,并使它们等于数组的众数(频率最高的元素)。
  • K组,阵列的最后一个元素永远属于K组,而第1元件将属于第1组。
  • 如果已正确找到K组,则问题将简化为使用最少操作将剩余数组划分为K-1 个组。

方法:这个问题可以用动态规划解决。

  1. DP(i, j)表示将数组 [1..i]划分为j 个组所需的最少操作。
  2. 现在,任务是找到DP(N, K) ,这是将数组 [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个基团。将array[1..(it – 1)]划分为K – 1 个组的代价是DP(it – 1, K – 1)并且可以计算出将array[it..N]划分为一个组的代价使用模式和它的频率观察。
  5. 要找到范围[it..i] 中出现次数最多的元素的频率,我们可以使用哈希图和整数变量。整数变量表示当前最高频率。该地图存储了迄今为止看到的所有元素及其频率。每当看到一个元素时,它在地图中的频率就会增加,如果现在这个元素的频率高于当前的最高频率,我们将当前的最高频率更新为刚刚看到的元素的频率。请参阅此方法。
  6. 因此, 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)