Algorithm 阵列中值变换最小步长

Algorithm 阵列中值变换最小步长,algorithm,Algorithm,给定一个数组A和n 整数。一次可以应用 对任何连续的 子阵列A[l..r]:分配给所有A i(l在每次迭代中,您可以将包含最大元素的子数组的大小加倍。在第一次迭代后,有一个大小为2的子数组包含最大元素。然后将您的操作应用于包含这2个元素的大小为4的子数组,得到包含最大元素的大小为4的子数组。然后应用于大小为8的子数组,并依此类推。你用log2(N)操作填充数组,这是最优的。如果N是30,五个操作就足够了 在最坏的情况下(即只有一个元素是最大的),这是最佳的,因为它在每次迭代中设置了尽可能多的元素

给定一个数组A和n 整数。一次可以应用 对任何连续的
子阵列A[l..r]:分配给所有A i(l在每次迭代中,您可以将包含最大元素的子数组的大小加倍。在第一次迭代后,有一个大小为2的子数组包含最大元素。然后将您的操作应用于包含这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的大小为2的子阵列上运行op。例如,[4…5]将获得:

[6 1 5 9 9 2 0 7 4 8]
现在在包含4…5的大小为4的子阵列上运行,例如在[2…5]上运行,以获得:

[6 9 9 9 9 2 0 7 4 8]
现在,在大小为8的子阵列上,例如[1…8],获取:

[9 9 9 9 9 9 9 9 4 8]
现在加倍可以得到16个9,但我们只有10个位置,所以这一轮是[1…10],得到:

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

更新3:由于这仅在最坏的情况下才是最佳的,因此它实际上并不是对原始问题的回答,该问题要求找到一种方法来找到所有输入的最小操作数。我将有关暴力强制的句子误解为关于使用中值操作的暴力强制,而不是找到最小值m操作顺序。

这是codechef Long竞赛的问题。由于竞赛已经结束,所以很抱歉,我正在粘贴问题解决方法(来源:CC竞赛编辑页)

“数组的任何状态都可以表示为二进制掩码,每个位1表示对应的数字等于max,否则等于0。您可以使用状态R[mask]和O(n)转换运行DP。您可以证明(或仅仅相信)当然,如果你运行良好的DP,statest的数量不会很大。我们DP的状态将是等于max的数字掩码。当然,仅对子数组[l;r]使用操作是有意义的,即1位的数量至少等于子掩码[l;r]中的0位的数量另外,你也应该注意到,如果你的操作的左边界是L,那么只使用最大可能的R(这就等于等于O(n)的数目等于它的数目),这是很好的。它对于C++代码使用地图结构来表示所有状态也是有用的。 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();
}
#包括
#包括
使用名称空间std;
int bc[1=当前最佳值)
{
如果(numMax*2>=n){
setMin(电流最佳,1+电流步数);
}
返回;
}  

如果(numMax<(1问题解决方法具有指数复杂度。对于N=30,它非常好。但对于较大的规模,情况并非如此。我认为,找到指数时间解决方案更有趣。我发现了一个,具有O(N4)复杂度

该方法利用了这样一个事实,即最优解从一组连续的最大元素开始,并仅扩展这一组元素,直到整个数组充满最大值

为了证明这一事实,取连续最大元素的两个起始组,并以最佳方式扩展每个组,直到它们合并为一个组。假设组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),它对不相等的群更有效。这个证明可以推广到多个群(多于两个)的情况

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

伪代码:
对于原始数组中的每组连续最大元素:
在矩阵中标记相应的元素,并清除其他元素
对于每个矩阵对角线,从一开始,包含此元素:
对于该对角线中的每个标记元素:
从该矩阵元素检索当前圈数
(使用此矩阵元素的索引初始化p1和p2)
p2=组的末尾
p1=组的开始
减小p1,同时可以将中值保持在最大值
(现在假定p1和p2之间的所有值为最大值)
而p2For 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