发现越来越序列[],最大限度地减少西格玛(ABS(A [1] + C [I]))序列、限度、西格玛、发现

2023-09-11 05:58:48 作者:微笑「浅浅

C N 整数指定数组;问题是要找到 N 整数 A的增加数组([1] - = A [1 + 1])这样,这和最小:

c is a given array of n integers; the problem is to find an increasing array of n integers a (a[i] <= a[i+1]) such that this sum is minimized:

abs(a[0]+c[0]) + abs(a[1]+c[1]) + ... + abs(a[n-1]+c[n-1])
// abs(x) = absolute value of x  

这是最佳的 A 只存在用整数做出出现在 C ,所以我们可以把它用DP在为O(n ^ 2):

An optimal a exists only made by integers appeared in c so we can solve it using DP in O(n^2):

dp[i][j]: a[i] >= j'th integer  

但应该有一个更快的解决方案,可能是为O(n LG N)

推荐答案

更新:我添加了解决方案,最大限度地减少加总-的绝对数值。其他的解决方案,最大限度地减少加总法广场,还在这里,在文章最后,如果有人有兴趣。

Update: I add the solution, which minimizes sum-of-absolute-values. Other solution, which minimizes sum-of-squares, is still here, at the end of this post, in case someone is interested.

予先从算法,即仅具有非负整数的阵列。然后,它会扩展到任何整数(或甚至非整数对象)。

I start with the algorithm, that works only with the array of non-negative integers. Then it will be extended to any integers (or even to non-integer objects).

这是一个贪心算法。它采用逐位重新$ P $整数psentation。开始与每个阵列的元件的最显著位(并忽略其他位为一段时间)。找到最大的preFIX,最大化的人/零平衡。现在,清除所有的数组值,属于preFIX并具有零最显著位(零这些值的所有位)。和在所有后缀数组值,即具有非零最显著位,设置所有其它位为1。采用下位为递归地应用此算法既preFIX和后缀最显著。

This is a greedy algorithm. It uses bitwise representation of integers. Start with the most significant bit of each array's element (and ignore other bits for a while). Find largest prefix, that maximizes ones/zeros balance. Now clear all the array values, belonging to prefix and having zero most significant bit (zero all bits of these values). And for all the array values in the suffix, that have non-zero most significant bit, set all other bits to "one". Apply this algorithm recursively to both prefix and suffix using next bit as "most significant".

这分裂原数组成段。你可以找到各段的中位数和填充输出数组这个中位数。另外,只需设置处理prefixes当对应的输出数组中位,让他们为零时后缀处理。

This splits the original array into segments. You can find median of each segment and fill the output array with this median. Alternatively, just set corresponding bits in the output array when processing prefixes and leave them zero when dealing with suffixes.

这一切工作,因为减少加总的绝对值需要找到子阵的中间,虽然发现此位,则可以非常近似比较值,总是使用整个阵列只有一个最显著位和下降到其它位后,为子阵。

All this works because minimizing sum-of-absolute-values requires to find the median of subarrays, and while finding this median, you can compare values very approximately, always using only a single most-significant bit for the whole array and descending to other bits later, for subarrays.

下面是C ++ 11 code段,这也解释了细节:

Here is C++11 code snippet, which explains the details:

//g++ -std=c++0x
#include <iostream>
#include <vector>
#include <iomanip>

using namespace std;
typedef vector<unsigned> arr_t;
typedef arr_t::iterator arr_it;

void nonincreasing(arr_it array, arr_it arrayEnd, arr_it out, int bits)
{
  if (bits != -1)
  {
    int balance = 0;
    int largestBalance = -1;
    arr_it prefixEnd = array;

    for (arr_it i = array; i != arrayEnd; ++i)
    {
      int d = ((*i >> bits) & 1)? 1: -1;
      balance += d;
      if (balance > largestBalance)
      {
        balance = largestBalance;
        prefixEnd = i + 1;
      }
    }

    for (arr_it i = array; i != prefixEnd; ++i)
    {
      *(out + (i - array)) += (1 << bits);
      if (!((*i >> bits) & 1))
      {
        *i = 0;
      }
    }
    nonincreasing(array, prefixEnd, out, bits - 1);

    for (arr_it i = prefixEnd; i != arrayEnd; ++i)
    {
      if ((*i >> bits) & 1)
      {
        *i = (1 << bits) - 1;
      }
    }
    nonincreasing(prefixEnd, arrayEnd, out + (prefixEnd - array), bits - 1);
  }
}

void printArray(const arr_t& array)
{
  for (auto val: array)
    cout << setw(2) << val << ' ';
  cout << endl;
}

int main()
{
  arr_t array({12,10,10,17,6,3,9});
  arr_t out(array.size());
  printArray(array);

  nonincreasing(begin(array), end(array), begin(out), 5);
  printArray(out);

  return 0;
}

要与任何整数工作,而不是只是阳性,有两种选择:

To work with any integers, not just positive, there are two alternatives:

找到最小整数输入数组中,并从其他元素减去它。当主算法完成,重新添加它(和否定的结果)。这使复杂度为O(N日志U),其中U是数组的值范围。 输入数组的紧凑值。排序是按价值计算,去除重复,并代替原有的值,用这个数组的索引。当与主算法完成的,改变索引回对应的值(和否定的结果)。这使复杂度为O(N日志高),其中H是独特的输入数组的值的数量。还允许这样不仅使用整数,但其可订购的任何对象(互相比较)。

下面是该算法的一个高层次的描述。复杂度为O(N)。

Minimize sum-of-squares algorithm

2019年ABS市场展望

Here is a high level description of this algorithm. Complexity is O(N).

开始一个子阵列的搜索,开始位于c []的开始,并具有最大的可能平均值。然后填入相同长度的子阵列中一个[]与此平均值(四舍五入至最接近的整数和否定)。然后,从删除该子阵列一个[]和c [](换言之,假定开始时的[]和c []向前移动由子阵列的长度)和递归此算法适用于其余部分的[]和c []。

Start with searching of a subarray, starting at the beginning of c[] and having largest possible average value. Then fill subarray of the same length in a[] with this average value (rounded to nearest integer and negated). Then remove this subarray from a[] and c[] (in other words, assume the beginning of a[] and c[] is moved forward by subarray's length) and recursively apply this algorithm to the remaining parts of a[] and c[].

该算法的最有趣的部分是寻找最大的子数组。填写一个临时数组b []的元素的累积和从C []: B [0] = C [0],B [1] = B [0] + C [1],.. ( - B [I】B【我+ M])/ M 。巧合的是,正是同样的公式(它的价值最大化)决定从B切线[我]的曲线,用b []描述。所以,你可以找到所有的最大值(以及子阵列边界),需要这个算法,同时,使用任何的凸包算法。凸包算法通常与分工作在两个方面,并有超线性的复杂性。但在这种情况下,点已经排序在一个维度,所以 Graham扫描或单调链算法做O(n)时间的任务,这也决定的复杂性整个算法。

Most interesting part of this algorithm is searching of largest subarray. Fill a temporary array b[] with cumulative sum of elements from c[]: b[0] = c[0], b[1] = b[0] + c[1], ... Now you can determine average of any interval in c[] with this: (b[i+m] - b[i]) / m. By coincidence, exactly the same formula (maximization of its value) determines a tangent line from b[i] to the curve, described by b[]. So you can find all maximum values (as well as subarray bounds), needed for this algorithm, at once, using any Convex hull algorithm. Convex hull algorithms usually work with points in two dimensions and have super-linear complexity. But in this case, points are already sorted in one dimension, so Graham scan or Monotone Chain algorithm do the task in O(N) time, which also determines complexity of the whole algorithm.

乙[] =集成(C []) H [] =凸形轮廓(B []) 在一个[] = - 微分(H [])

Visualization of the example array processing: