给定n个整数类型元素的向量,产生最小数量的转换步骤的效率更高的算法是什么,导致所有元素等于的向量,知道:
示例:
Given :
0, 3, 0
Then 2 steps are required :
1, 2, 0
1, 1, 1
Given :
3, 0, 3
Then 1 step is required :
2, 2, 2
Given :
4, 0, 0, 0, 4, 0, 0, 0
Then 3 steps are required :
3, 1, 0, 0, 3, 1, 0, 0
2, 1, 1, 0, 2, 1, 1, 0
1, 1, 1; 1, 1, 1, 1, 1
我当前的算法基于元素每一边的整数之和。但我不确定它是否产生了最小的步骤。
仅供参考,问题是代码竞赛的一部分(由Criteo http://codeofduty.criteo.com创建)已结束。
答案 0 :(得分:6)
这是一种方式。您知道数组的总和,因此您知道每个单元格中的目标编号。 因此,您还知道每个子阵列的目标总和。 然后遍历数组,并在每一步做出决定:
重复此操作,直到不再进行任何更改(即您只为每个元素应用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;
}
答案 1 :(得分:4)
我有个主意。我不确定它会产生最佳结果,但感觉就像它可以。
假设初始向量是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]
,只需随机转移一次,就不会更改最终结果。< / p>
答案 2 :(得分: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;
}