以下是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
我该如何解决这个问题?
答案 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)
你必须找到一个
的序列在约束下是一个凸整数优化问题,似乎非常困难才能最佳地解决。
答案 5 :(得分:0)
我有以下想法:
合并:合并两个组总是有两种可能性,即调整 左侧组或调整右侧组。我将在示例中展示这一点, 这个想法应该是明确的。
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