让我们先解决一点不同的问题:
应将min 的最小数量添加到value 以确保1 在kth 位置(从零开始)?
我们这里有两种情况:
如果value 在k 位置有1,我们添加0(什么都不做);
如果value 在k 位置有0,我们可以添加
min = 100...000000000 - (value & 11.....11111)
<- k zeroes -> <- k ones ->
代码(C#)
private static long AddToEnsureOne(long value, int position) {
if ((value & (1L << position)) != 0)
return 0;
long shift = 1L << (position);
return shift - (value & (shift - 1));
}
演示:如果我们有 3 并且我们希望 1 在第二个位置
0b011
^
we want 1 here
我们应该添加
0b100 - (0b011 & 0b11) == 4 - 3 == 1
让我们添加:3 + 1 == 4 == 0b100,1 在第二个位置
现在我们可以扫描所有 32 个位置(如果整数是好的旧 32 位整数 int); C#代码:
private static long MinToAdd(IEnumerable<int> items) {
long best = 0;
for (int i = 0; i < 32; ++i) {
long sum = 0;
foreach (int item in items)
sum += AddToEnsureOne(unchecked((uint)item), i); // uint - get rid of sign
if (i == 0 || sum < best)
best = sum;
}
return best;
}
可以改进解决方案的循环不是针对32 位置,而是针对最大item 中最左边的1。这里我们以4为最大值,也就是0b100,最左边的1在2的位置;因此for (int i = 0; i <= 2; ++i) 在上下文中就足够了
简单测试:
Console.Write(MinToAdd(new int[] { 4, 4, 3, 2}));
结果:
3