分而治之:计算经过的时间



我必须在大学里做一项小作业:

我有一个运行独立服务的服务器。所有这些服务过去都是在同一时间开始的。每个服务"i"在特定时间段(以秒为单位)后将"b[i]"行写入服务器上的日志文件。输入由日志文件的行数"l"和服务数"n"组成。然后,我们在接下来的"n"行中为每个服务i:"s[i]'"是前面提到的周期,"b[i]'是服务写入日志文件的行数。

我必须根据日志文件中的行数,以秒为单位计算程序开始运行的时间。示例:

input:  
19 3 
7 1
8 1
10 2
Output: 
42

我必须使用分而治之,但我甚至不知道如何将其分解为子问题。我还必须使用这个函数,其中ss是服务的周期数组,bs是每个服务写入日志文件的行数:

long linesAt(int t, int[] ss, int[] bs) {
  long out = 0;
  for (int i = 0; i < ss.length; i++) {
     // floor operation
     out += bs[i] * (long)(t/ss[i]);
  }
  return out;

ss和bs基本上是输入的数组,如果我们举一个例子,它们看起来是这样的,上面的行是数组的索引:

ss:

0 1 2
7 8 10

bs:

0 1 2
1 1 2

很容易看出,42应该是输出

 linesAt(42) = floor(42/7)*1+floor(42/8)*1+floor(42/10)*2 = 19

现在我要写一个函数

int solve(long l, int[] ss, int[] bs)

我已经用蛮力写了一些伪代码,但我不知道如何用分治范式来解决这个问题,我的伪代码看起来是这样的:

Solve(l, ss, bs)
  out = 0
  t = 0
  while (out != l)
    out = linesAt(t, ss, bs)
    t++
  end while
  return t

我想我必须以某种方式拆分l,以便计算较小长度的时间。但我真的不知道该怎么做,因为当你看到这个时,它似乎不可能:

t           out
0..6        0
7           1
8           2
9           2
10          4
11..13      4
14          5
15          5
16          6
17..19      6
20          8
...
40          18
42          19

尚塔尔。

听起来像是一个经典的二进制搜索符合要求,需要先执行一个步骤来获得合适的最大值。您从对时间't'(比如100)的一些估计开始,并调用linesAt来获得该t的行。如果返回的值太小(即小于l),请将"t"加倍,然后重试,直到行数过大为止。

在这一点上,您的maximumtminimumt/2。然后你重复:

  • 选取t作为maximumminimum之间的中点
  • 调用linesAt(t,...)获取线路数
  • 如果你找到了目标,就停下来
  • 如果行数过多,请调整最大值:maximum = t
  • 如果行数太少,请调整最小值:minimum = t

上面的算法是一个二进制搜索——它在每次迭代中将搜索空间一分为二。因此,它是分而治之的一个例子。

您正试图求解一个整数方程:

 floor(n/7)*1+floor(n/8)*1+floor(n/10)*2 = 19

你可以去掉floor函数,求解n,得到一个下界和上界,然后在这两个边界之间搜索。

求解以下方程:

(n/7)*1+(n/8)*1+(n/10)*2 = 19
n=19/(1/7+1/8+2/10)

已经找到n,m0的值的哪个范围将使得floor (m0 / 7) = floor (n/7)

floor (n/7) * 7 <= m0 <= (ceiling (n/7) * 7) - 1

以同样的方式,计算m1和m2。

对于1到3之间的i,取max(mi)为上限,取min(mi)作为下限。

在这一点上进行二进制搜索可能会有些过头了。

最新更新