如何以最少的步数使序列成为非递减序列?

时间:2011-04-27 04:59:51

标签: algorithm sorting dynamic-programming

以下是problem状态

  

给出一个N个整数序列。在每一步中,允许将任意数字的值增加1或将其减少1.游戏的目标是使序列不减少且步数最小

例如,Given

  

3 2 -1 2 11

可以使该序列在 4 步骤中成为非递减序列(将3减1并将-1增加3)。

 (-1) (0) (+3) (0) (0)

序列将变为

2 2 2 2 11

我该如何解决这个问题?

6 个答案:

答案 0 :(得分:2)

我在C#中提供了工作代码。它可以轻松移植到您选择的语言。时间复杂度约为n2。如果count大于minimumValue,则可以在方法GenerateSequenceForEveryIndex()中进行优化。


using System;
using System.Collections.Generic;

namespace ConsoleApplication2
{
    class Program
    {
        static void Main(string[] args)
        {
            Sequence seq = new Sequence();
            seq.GenerateSequenceForEveryIndex();
            Console.ReadLine();
        }

    }

    class Sequence
    {
        int count;
        public Sequence()
        {
            // Get Number of inputs
            Console.WriteLine("Number of values? ");
            this.count = int.Parse(Console.ReadLine());
            Console.WriteLine("Enter input values followed by RETURN/ENTER");
            GetInputSequence();
        }

        List<int> inputSequence = new List<int>();
        private void GetInputSequence()
        {
            for (int index = 0; index < count; index++)
                inputSequence.Add(int.Parse(Console.ReadLine()));
        }

        internal void GenerateSequenceForEveryIndex()
        {
            int minimumValue = 0;
            for (int index = 0; index < count; index++)
            { 
                // Get output sequence for every index
                // You can make a decision to get the minimum of the moves
                int newValue = GenerateSequenceForCurrentIndex(index);
                if (minimumValue == 0 || minimumValue > newValue) minimumValue = newValue;
                Console.WriteLine("Number of moves: " + newValue);
                Console.WriteLine();
            }
            Console.WriteLine("Minimum number of moves: " + minimumValue);
        }

        private int GenerateSequenceForCurrentIndex(int index)
        {
            int numberOfMoves = 0;
            int[] newOutputSequence = new int[count];
            int[] differenceSequence = new int[count];
            this.inputSequence.CopyTo(newOutputSequence);
            for (int ind = 0; ind < count; ind++)
            {
                if (ind == index) continue;
                differenceSequence[ind] = (ind == 0 ? newOutputSequence[index] : newOutputSequence[ind - 1])
                    - newOutputSequence[ind];

                // If sorted as non-decreasing, continue
                if (ind > index && differenceSequence[ind] < 0) continue;

                numberOfMoves += Math.Abs(differenceSequence[ind]);
                newOutputSequence[ind] += differenceSequence[ind];

            }
            DisplaySequence(differenceSequence, "Diff Sequence: ");
            DisplaySequence(newOutputSequence, "New Sequence: ");
            return numberOfMoves;
        }

        private static void DisplaySequence(int[] newOutputSequence, string heading)
        {
            Console.Write(heading);
            for (int i = 0; i < newOutputSequence.Length; i++)
                Console.Write(newOutputSequence[i] + " ");
            Console.WriteLine();
        }
    }
}

算法说明 每个元素值都可以作为一个透视值,这意味着它左边的值应该等于它自己的值,而右边的值应该大于或等于它自己的值。 已经说过,最多可以有'n'个唯一的非下降序列。 现在algorigthm获取每个值(参见方法GenerateSequenceForEveryIndex)并生成新的非降序序列。

在GenerateSequenceForCurrentIndex()内部,索引左侧的值确保等于array [index]。我们不必担心不会因为不同的序列(当索引&lt;当前索引)时已经注意到了。确保右侧的值大于或等于当前值。任何差异都被视为额外的移动(绝对值)

最后,DisplaySequence()只显示序列中的值。

答案 1 :(得分:1)

问题是,您应该争取最小数量的更改。 可以说最后一个数字是-1000000。 如果顺序执行序列,则最终必须将1000002添加到最后一个元素以获得非递减序列,但解决方案将无法满足使用最少步骤数的要求。 因此,一次运行序列,记录元素之间的差异可能是个好主意。希望你抓住我的漂移。 (我的写作不是很明确,因为我的想法在我自己看来: - )

答案 2 :(得分:1)

#include <stdio.h>
#include <stdlib.h>

int get_destination( int numbers[], int size ) {
    int i,j;
    int destination = 0;
    int swap_done = 0;
    for ( i = 0; i < size - 1; i++) {
        for (j = 0; j < size - 1; j++) {
            if ( numbers[j] > numbers[j + 1]){
                destination = j + 1;
                swap_done = 1;
            }
        }
        if ( swap_done ) {
                break;
        }
    }
    return destination;
}
int main( int argc, char **argv ) {
    #define SIZE 5
    //int numbers[SIZE]= {3, 2, -1, 2, 11};
    //int numbers[SIZE]= {1,2,3,4,5};
    int numbers[SIZE]= {2, 1, 1, 1, 1};
    int answer = 0;
    int dest = get_destination( numbers, SIZE);
    if ( dest ) {
        for ( int i = 0; i < SIZE - 1; i++) {
            answer = answer + abs( abs(numbers[i]) - abs(numbers[dest]));
        }
    }
    printf ( "ANSWER = %d\n", answer );
    return 0;
}

如果你看一下冒泡排序,在外循环的第一次传递中,它将元素放在正确的位置,我试图使用这个概念。一旦找到第一个传递交换位置,使用它作为参考,并根据该元素调整序列中的所有元素。

答案 3 :(得分:1)

解决方案可在以下位置找到: http://codeforces.com/blog/entry/364

它说 -

注意,存在非递减序列,可以使用最小数量的移动从给定序列获得,并且其中所有元素等于来自初始序列的某个元素(即,其仅包含来自初始序列的数字)最初的序列)。

证明 -

假设没有最佳序列,其中每个元素等于初始序列中的某个元素。然后有一个元素i不等于{ai}的任何元素。如果具有数字i-1和i + 1的元素不等于元素i,那么我们可以将它移近ai并且答案将减少。因此存在一个相等元素块,并且它们都不等于初始序列的任何元素。注意,我们可以将所有块增加1或将其减少1,并且其中一个动作不会增加答案,因此我们可以向上或向下移动此块,直到其所有元素都等于初始序列中的某个元素。

ALGORITHM -

假设{ai}是初始序列,{bi}是相同的序列,但是其中所有元素都是不同的,并且它们从最小到最大排序。设f(i,j)是获得其中第一个i元素不递减且第i个元素最多为bj的序列所需的最小移动次数。在这种情况下,问题的答案将等于f(n,k),其中n是{ai}的长度,k是{bi}的长度。我们将使用以下重现来计算f(i,j):

f(1,1)=|a1-b1|
f(1,j)=min{|a1-bj|,f(1,j-1)},  j>1
f(i,1)=|ai-b1|+f(i-1,1),  i>1
f(i,j)=min{f(i,j-1),f(i-1,j)+|ai-bj|},  i>1, j>1

复杂性为O(N2)。为了避免内存限制,应该注意,要计算f(i,),您只需要知道f(i-1,)和已计算的第i行的部分。

答案 4 :(得分:0)

你必须找到一个

的序列
  1. 积分
  2. 不减少
  3. 尽可能接近原始的L ^ 1范数
  4. 在约束下是一个凸整数优化问题,似乎非常困难才能最佳地解决。

答案 5 :(得分:0)

我有以下想法:

  1. 构建非递减序列组(在您的示例中) {3} {2} {-1 2 11})
  2. 合并两个组,这会将组数减少一到两个。
  3. 如果只有一个组,则找到非递减解决方案。 如果没有,请返回步骤2.
  4. 合并:合并两个组总是有两种可能性,即调整 左侧组或调整右侧组。我将在示例中展示这一点, 这个想法应该是明确的。

    Ajust正确的小组:

       {2} {-1 2 11} --> {2} {2 2 11}
       {2} {-1 0 1}  --> {2} {2 2 2}
    

    调整左组:

       {3}   {2} --> {2}   {2}
       {2 3} {1} --> {1 1} {1}
    

    因为总有两种方法可以去(向右调整/调整左侧组) 你可以使用回溯算法中的步骤(它记住最好的 到目前为止找到的解决方案。)

    示范:

    {3} {2} {-1 2 11}
    adjust left group --> {2 2} {-1 2 11}
    adjust left group --> {-1 -1 -1 2 11}
    

    <强>解决方案:

    {-1 -1 -1  2 11} (adjust left, adjust left)   --> score 7
    { 2  2  2  2 11} (adjust left, adjust right)  --> score 4 (best)
    {-1 -1 -1  2 11} (adjust right, adjust left)  --> score 7
    { 3  3  3  3 11} (adjust right, adjust right) --> score 6