数组中值变换最小步长

时间:2012-05-08 13:07:40

标签: algorithm

给定一个带有n的数组A. 整数。在一个回合中可以应用 以下操作连续进行 子阵列A [l..r]:分配给所有A i(l< = i< = r) 子阵A [l..r]的中位数。 设max为A的最大整数。 我们想知道最低限度 改变A所需的操作次数 到n个整数的数组,每个整数都有值 最大。 例如,设A = [1,2,3]。我们想将其改为[3,3,3]。我们 可以在两个操作中执行此操作,首先是 子阵A [2..3](之后A等于[1, 3,3]),然后操作到A [1..3]。 此外,如下为某些阵列A定义中值。设B是一样的 数组A,但以非递减方式排序 订购。 A的中位数是B m(基于1 索引),其中m等于(n div 2)+1。 这里'div'是一个整数除法运算。 因此,对于具有5个元素的排序数组, 中位数是第3个元素,也是排序的元素 有6个元素的数组,它是第4个元素。

由于N的最大值是30.我想到了暴力迫使结果 可以有更好的解决方案。

3 个答案:

答案 0 :(得分:5)

您可以将每次迭代中包含最大元素的子数组的大小加倍。在第一次迭代之后,有一个大小为2的子数组包含最大值。然后将您的操作应用于大小为4的子数组,包含这两个元素,为您提供一个包含最大值的子数组4。然后应用于8号子阵列,依此类推。您在log2(N)操作中填充数组,这是最佳的。如果N是30,则五次操作就足够了。

这在最坏的情况下是最佳的(即,当只有一个元素是最大值时),因为它在每次迭代中设置尽可能多的元素。

更新1 :我注意到我搞砸了4s和8s。校正。

更新2 :这是一个例子。数组大小10,开始状态:

[6 1 5 9 3 2 0 7 4 8]

要获得两个9,请在包含9的子大小的子数组上运行op。例如A [4 ... 5]得到你:

[6 1 5 9 9 2 0 7 4 8]

现在运行在包含4 ... 5的大小为4的子阵列上,例如在A [2 ... 5]上得到:

[6 9 9 9 9 2 0 7 4 8]

现在在大小为8的子阵列上,例如A [1 ... 8],得到:

[9 9 9 9 9 9 9 9 4 8]

现在加倍我们会得到16个9,但是我们只有10个位置,所以A [1 ... 10]一轮,得到:

[9 9 9 9 9 9 9 9 9 9]

更新3 :由于这只是最坏情况下的最佳选择,因此它实际上不是原始问题的答案,原始问题要求找到所有输入的最小操作数。我误解了关于暴力迫使强制执行中位数操作的句子,而不是找到最小操作序列。

答案 1 :(得分:2)

这是来自codechef Long Contest的问题。由于比赛已经结束,所以尴尬,我正在粘贴问题制定者方法(来源:CC竞赛编辑页)。

“数组的任何状态都可以表示为二进制掩码,每个位1表示相应的数字等于最大值,否则为0。您可以使用状态R [掩码]和O(n)传输来运行DP。你可以证明(或者只是相信),当你运行良好的DP时,状态的数量不会很大。我们的DP的状态将是等于最大数字的掩码。当然,它是有意义的仅对这种子阵列使用操作[l; r] 1位的数量至少与子掩码[l; r]中的0位数一样多,因为否则什么都不会改变。另外你应该注意到如果左边操作的界限是l最好只使用最大可能的r进行操作(这使得转换的数量等于O(n))。对于C ++编码器来说,使用地图结构来表示所有状态也是有用的。“< / p>

C / C ++代码是::

#include <cstdio>
#include <iostream>
using namespace std;

int bc[1<<15];
const int M = (1<<15) - 1;

void setMin(int& ret, int c)
{
    if(c < ret) ret = c;
}

void doit(int n, int mask, int currentSteps, int& currentBest)
{
    int numMax = bc[mask>>15] + bc[mask&M];
    if(numMax == n) {
        setMin(currentBest, currentSteps);
        return;
    }
    if(currentSteps + 1 >= currentBest)
        return;
    if(currentSteps + 2 >= currentBest)
    {
        if(numMax * 2 >= n) {
            setMin(currentBest, 1 + currentSteps);
        }
        return;    
    }  

    if(numMax < (1<<currentSteps)) return;

    for(int i=0;i<n;i++) 
    {
        int a = 0, b = 0;
        int c = mask;
        for(int j=i;j<n;j++)
        {
            c |= (1<<j);
            if(mask&(1<<j)) b++;
            else a++;
            if(b >= a) {
                doit(n, c, currentSteps + 1, currentBest);
            }
        }
    }
}

int v[32];
void solveCase() {
    int n;
    scanf(" %d", &n);
    int maxElement = 0;
    for(int i=0;i<n;i++) {
        scanf(" %d", v+i);
        if(v[i] > maxElement) maxElement = v[i];
    }
    int mask = 0;
    for(int i=0;i<n;i++) if(v[i] == maxElement) mask |= (1<<i);
    int ret = 0, p = 1;
    while(p < n) {
        ret++;
        p *= 2;
    }
    doit(n, mask, 0, ret);
    printf("%d\n",ret);
}

main() {
    for(int i=0;i<(1<<15);i++) {
        bc[i] = bc[i>>1] + (i&1);
    }
    int cases;
    scanf(" %d",&cases);
    while(cases--) solveCase();
}

答案 2 :(得分:1)

问题制定者方法具有指数复杂性。 N = 30非常好。但对于较大尺寸则不是这样。我认为,找到一个指数时间解决方案会更有趣。我找到了一个,O(N 4 )的复杂性。

这种方法使用这样一个事实,即最优解决方案从一组连续的最大元素开始,并且只扩展这个单独的组,直到整个数组充满最大值。

为证明这一事实,取2个连续最大元素的起始组,并以最佳方式扩展它们,直到它们合并为一个组。假设组1需要X圈增长到M大小,组2需要Y轮增长到相同大小M,并且轮流X + Y + 1这些组合并。结果是一组大小至少为M * 4.现在不是为组2转Y,而是为组1再做一个X + 1.在这种情况下,组大小至少为M * 2且最多为M / 2 (即使我们计算最初的最大元素,也可能包含在步骤Y中)。在此更改之后,在转弯X + Y + 1时,合并的组大小仅作为第一组扩展的结果至少为M * 4,从第二组添加至少一个元素。因此,在此处扩展单个组会以相同的步数生成更大的组(如果Y> 1,则甚至需要更少的步骤)。由于这适用于相同的组大小(M),因此对于非平等组,它将更好地工作。这个证据可以扩展到几个小组(两个以上)的情况。

要处理单组连续的最大元素,我们需要只跟踪两个值:组的起始位置和结束位置。这意味着可以使用三角矩阵来存储所有可能的组,从而允许使用动态编程算法。

的伪代码:

For each group of consecutive maximal elements in original array:
  Mark corresponding element in the matrix and clear other elements
  For each matrix diagonal, starting with one, containing this element:
    For each marked element in this diagonal:
      Retrieve current number of turns from this matrix element
      (use indexes of this matrix element to initialize p1 and p2)
      p2 = end of the group
      p1 = start of the group
      Decrease p1 while it is possible to keep median at maximum value
      (now all values between p1 and p2 are assumed as maximal)
      While p2 < N:
        Check if number of maximal elements in the array is >= N/2
          If this is true, compare current number of turns with the best result \
               and update it if necessary
          (additional matrix with number of maximal values between each pair of
            points may be used to count elements to the left of p1 and to the
            right of p2)
        Look at position [p1, p2] in the matrix. Mark it and if it contains \
            larger number of turns, update it
        Repeat:
          Increase p1 while it points to maximal value
          Increment p1 (to skip one non-maximum value)
          Increase p2 while it is possible to keep median at maximum value
        while median is not at maximum value

为了保持算法简单,我没有提到当组在位置0开始或在位置N结束,跳过初始化并且没有进行任何优化时的特殊情况。