有效计算所有给定总和的对

时间:2018-09-01 16:27:51

标签: java python algorithm time-complexity

我遇到了以下问题。

  

给出一个列表,其中每个项目代表以歌曲的秒数表示的持续时间,返回成对的歌曲总数,这样它们的持续时间总计为分钟(例如1m0s,2m0s等)。

示例:
输入:[10,50,20,110,40]
输出:3(考虑索引为(0,1),(0,3),(2,4)的对)

我只能考虑一种蛮力的方法,在这种方法中我要考虑所有的歌曲对。这种方法的时间复杂度为O(n ^ 2)。
有更好的方法吗?

2 个答案:

答案 0 :(得分:1)

  1. 给定的问题可以简化为以下事实:我们需要发现对(a,b),以便从给定列表A中找到(a + b) mod 60 == 0
  2. 观察#1:对于任何整数x,(x mod 60)都从o到59。
  3. 初始化一个长度为60的数组,其默认值设置为0,其索引 i 将存储列表A中的元素数,使得所有元素的x mod 60 = i x属于A

    int freq[60] = {0};
    for(int i = 0; i < A.size(); i++)
      freq[(A[i] % 60)]++;
    
  4. 现在再次遍历数组A,对于每个x,我们需要从累积频率图中获取索引60 - (x mod 60)的计数,该计数对应于它可以形成一对的元素的数量用。在(x mod 60) == 30会很棘手的情况下,这将要求我们从频率计数中减去1。

    int ans = 0;
    for(int i = 0; i < A.size(); i++) {
      ans += freq[60 - (A[i] % 60)];
      if(A[i] % 60 == 30) ans--;
    }
    

解决方案的整体复杂度为O(n)。

答案 1 :(得分:0)

考虑一下散列,创建存储桶和模除的行。所有可能的分钟数将进入60个可能的存储桶之一。然后考虑从任意两个存储桶中选择两个第二个值时可能有多少种组合。然后使用nC2进行计数。这是我在Java中的解决方案。

public int numPairsDivisibleBy60(int[] time) {
    int k = 60;
    int[] mods = new int[k];
    for (int i = 0; i < time.length; i++) 
        mods[time[i] % k]++;
    // n(n-1)/2 pairs for multiples of k and numbers which leave remainder as half multiple of k
    int count = ((mods[0] * (mods[0] - 1)) / 2) +
                ((mods[k / 2] * (mods[k / 2] - 1)) / 2);
    for (int i = 1; i < k / 2; i++)
        count += mods[i] * mods[k - i];
    return count;
}