什么是更有效的算法来均衡向量?

问题描述

| 给定一个由n个类型为整数的元素组成的向量,哪种方法能产生最少的转换步骤,从而使所有元素均相等的向量更有效? 在一个步骤中,您最多可以将一个元素从一个元素转移到其相邻元素([0,3,0]-> [1、2、0]可以,但是[0,3,0]-> [1, 1,1])。 在一个步骤中,一个元素可以收到2个点:一个来自其左侧邻居,一个来自右侧([3,0,3]-> [2,2,2])。 first元素和last元素只有一个邻居,分别是2nd元素和n-1元素。 一个元素在任何步骤都不能为负。 例子 :
Given :
 0,3,0
Then 2 steps are required :
 1,2,0
 1,1,1

Given :
 3,3
Then 1 step is required :
 2,2

Given :
 4,4,0
Then 3 steps are required :
 3,0
 2,1; 1,1
我当前的算法基于元素每一侧的整数之和。但是我不确定它是否能产生最少的步骤。 仅供参考,问题是代码竞赛(由Criteo http://codeofduty.criteo.com创建)的一部分,已经结束。     

解决方法

        这是一种方法。您知道数组的总和,因此知道每个单元格中的目标数。 因此,您还知道每个子数组的目标总和。 然后遍历数组,并在每个步骤上进行设计: 向左移动1:如果与前一个元素的和小于期望值。 向右移动1:如果当前元素的总和超过期望值 什么也不要做:如果以上两项都为假 重复此操作,直到没有更多更改为止(即,您仅对每个元素应用了3)。
    public static int F(int[] ar)
    {
        int iter = -1;
        bool finished = false;
        int total = ar.Sum();

        if (ar.Length == 0 || total % ar.Length != 0) return 0; //can\'t do it
        int target = total / ar.Length;

        int sum = 0;

        while (!finished)
        {
            iter++;
            finished = true;
            bool canMoveNext = true;

            //first element
            if (ar[0] > target)
            {
                finished = false;
                ar[0]--;
                ar[1]++;

                canMoveNext = ar[1] != 1;
            }

            sum = ar[0];
            for (int i = 1; i < ar.Length; i++)
            {
                if (!canMoveNext)
                {
                    canMoveNext = true;
                    sum += ar[i];
                    continue;
                }

                if (sum < i * target && ar[i] > 0)
                {
                    finished = false;
                    ar[i]--;
                    ar[i - 1]++;
                    sum++;
                }
                else if (sum + ar[i] > (i + 1) * target && ar[i] > 0) //this can\'t happen for the last element so we are safe
                {
                    finished = false;
                    ar[i]--;
                    ar[i + 1]++;

                    canMoveNext = ar[i + 1] != 1;
                }

                sum += ar[i];
            }
        }

        return iter;
    }
    ,        我有一个主意。我不确定会产生最佳结果,但感觉可以。 假设初始向量是N大小的向量
V
。您需要另外两个N大小的向量: 在
L
向量中,从左开始对元素求和:
L[n] = sum(i=0;i<=n)  V[n]
R
向量中,从右开始对元素求和:
R[n] = sum(i=n;i<N) V[n]
最后,您需要一个最后的特定值:V的所有元素之和应等于
k*N
,其中
k
是一个整数。你有
L[N-1] == R[0] == k*N
让我们取
L
矢量。这个想法是,对于任何n,考虑
V
向量分为两部分,一个从0到n,另一个包含其余部分。如果为
L[n]<n*k
,则必须用第二部分的值“填充”第一部分。反之亦然,如果
L[n]>n*k
。如果
L[i]==i*k
,那么恭喜您,问题可以细分为两个子问题!没有理由将第二个向量中的任何值都转移到第一个向量中,反之亦然。 然后,该算法很简单:对于n的每个值,检查
L[n]-n*k
R[n]-(N-n)*k
的值并采取相应的措施。只有一种特殊情况,如果
L[n]-n*k>0
R[n]-(N-n)*k>0
(V [n]处有一个高值),则必须在两个方向上将其清空。只需随机选择一个转移方向即可。 当然,不要忘记相应地更新
L
R
。 编辑:实际上,看来您只需要
L
向量。这是一个简化的算法。 如果
L[n]==n*k
,什么都不做 如果
L[n]<n*k
,则将一个值从
V[n+1]
转移到
V[n]
(如果
V[n+1]
> 0当然是) 如果
L[n]>n*k
,则将一个值从
V[n]
转移到
V[n+1]
(如果
V[n]
> 0当然是) 并且(特殊情况下),如果您被要求从
V[n]
转移到
V[n-1]
V[n+1]
,只需随机转移一次,它不会改变最终结果。     ,        感谢Sam Hocevar,为fiver的以下替代实现提供了以下方法:
public static int F(int[] ar)
{
    int total = ar.Sum();

    if (ar.Length == 0 || total % ar.Length != 0) return 0; //can\'t do it
    int target = total / ar.Length;

    int[] left = new int[ar.Length];
    int[] right = new int[ar.Length];
    int maxshifts = 0;
    int delta = 0;
    for (int i = 0; i < ar.Length; i++)
    {
        left[i] = delta < 0 ? -delta : 0;
        delta += ar[i] - target;
        right[i] = delta > 0 ? delta : 0;
        if (left[i] + right[i] > maxshifts) {
            maxshifts = left[i] + right[i];
        }    
    }

    for (int iter = 0; iter < maxshifts; iter++)
    {
        int lastleftadd = -1;

        for (int i = 0; i < ar.Length; i++)
        {
            if (left[i] != 0  && ar[i] != 0)
            {
                ar[i]--;
                ar[i - 1]++;
                left[i]--;
            }
            else if (right[i] != 0 && ar[i] != 0
                              && (ar[i] != 1 || lastleftadd != i))
            {
                ar[i]--;
                ar[i + 1]++;
                lastleftadd = i + 1;
                right[i]--;
            }
        }
    }

    return maxshifts;
}
    

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...