1、初始分析:设最优的解法中,第i台洗衣机需要递的次数是itime。则最少移动次数为所有itime中的最大值。
举个简单的栗子整体感受一下
比如4台洗衣机瞭衣服数为3 4 4 4 5 则每一台洗衣机需要递的次数都是1。
具体操作为5给4一件,4给3一件,3给2一件,2给1一件。总共一次操作但分析后可以发现,这步操作可以简化为5给1一件。
这样我们就得出一个结论:分析一台洗衣机时,只需要分析它左边的总情况和右边的总情况,而不用管每一边内部具体的细节。
我们先设定几个参数。衣服总数sum 洗衣机总台数 n 平均值av
左边缺少的衣服数 A = av * i - LeftSum,右边缺少的衣服数 B = av * ( n - 1 - i ) - RightSum。(如果多则为负值)
2、对于第i台洗衣机来说,总共有五种情况
①左右都缺,都需要从i号洗衣机递给她们,因为每次只能递一件,所以itime为A+B
②左右都多了,说明它们都得给i号洗衣机递,因为两边可以同时递,所以递的次数取决于需要调整次数多的那一边,即itime=MAX (-A,-B)
③左右都不缺也不多,这个很简单,i不用动,itime = 0
④一边的多一边少,但是多的量比少的量多,说明此时i里的衣服少了,多的那一个既需要给i,又需要给i的另一边。无论给谁,都得经过i,所以递的次数为多的量,即itime = MAX (|A|,|B/)
⑤一边的多一边少,但是少的量比多的量多,说明此时i里的衣服多了,少的那一边可以从i里取,也可以从多的那一边取。无论从哪取,都得经过i,所以递的次数为少的量,即itime =
MAX ( |Al,|B|) 。
④和⑤导致的结果一样,可以合为一种情况。
用这种方法求出所有的itime,最大值即为最少移动次数。
附上咱写的C++代码作参考。
#define MAX(A,B) ((A)>(B))?(A):(B)
int findMinMoves(vector<int>& machines)
{
int sum = 0;
for (int e : machines)
{
sum += e;
}
if (sum%machines.size())
return -1;
int av = sum / machines.size();
int i = 0;
int iLeftSum = 0;
int iRightSum = sum - machines[i];
int ret = 0;
while (i < machines.size())
{
//左边需要增加的量A
int LeftLack = i*av - iLeftSum;
//右边需要增加的量B
int RightLack = (machines.size() - 1 - i)*av - iRightSum;
//5种情况:1.左右都缺,都需要从i号洗衣机递给她们 A+B
// 2.左右都多,都需要送给i号洗衣机,可以同时给,所以经过i号洗衣机的次数是A和B的较大值
// 3.左右都不缺,i号洗衣机ret=0;
// 4.一边缺,一边多,i号洗衣机多。i需要递的次数为需求多的一边的量
// 5.一边缺,一边多,i号洗衣机少,i需要递的次数为需要多的一边的量。
if (LeftLack > 0 && RightLack > 0)
{
ret = MAX(ret, machines[i] - av);
}
else if (LeftLack < 0 && RightLack < 0)
{
ret = MAX(ret, MAX(-LeftLack, -RightLack));
}
else if (LeftLack + RightLack == 0)
{
ret = MAX(ret, 0);
}
else
{
ret = MAX(ret, MAX(abs(LeftLack), abs(RightLack)));
}
iLeftSum += machines[i];
++i;
if (i<machines.size())
iRightSum -= machines[i];
}
return ret;
}