【问题标题】:Intuition behind getting distance from the end of vector versus just counting距离向量末端的距离而不是仅仅计数背后的直觉
【发布时间】:2021-02-11 15:56:05
【问题描述】:

我正在尝试解决GeeksForGeeks question 如下:

计算将一个数组分成三个总和相等的连续部分的方法的数量:给定一个包含n 数字的数组,找出ij 这样的索引对的个数0i-1 的元素之和等于从ij 的元素之和等于从j+1n-1 的元素之和。对于输入{1, 2, 3, 0, 3},输出为2(我们有索引:(0, 1), (2, 2) and (3, 4) and (0, 1), (2, 3) and (4 , 4))

代码如下:

// C++ program to count number of ways we can 
// partition an array in three parts with equal 
// sum. 
#include<bits/stdc++.h> 
using namespace std; 

// binary search to find the number of required 
// indices in suffix array. Returns index of 
// first element which is greater than x. 
int binarysearch(vector <int> &v, int x) 
{ 
    int low = 0, high = v.size()-1; 
    while (low <= high) 
    { 
        int mid = (low + high)/2; 
        if (v[mid] <= x) 
            low = mid + 1; 
        else if (v[mid] > x && v[mid-1] <= x) 
            return mid; 
        else if (v[mid] > x && mid == 0) 
            return mid; 
        else
            high = mid-1; 
    } 
    return -1; 
} 

// function to calculate the number of ways to 
// divide the array into three contiguous parts 
int CountContiguousParts(int arr[] ,int n) 
{ 
    int count = 0; // initializing answer 

    // Creating and filling prefix array 
    int prefix[n]; 
    prefix[0] = arr[0]; 
    for (int i=1; i<n; i++) 
        prefix[i] = prefix[i-1] + arr[i]; 

    // Total sum of elements is equal to last 
    // value in prefix array. 
    int total_sum = prefix[n-1]; 

    // If sum of all the elements is not divisible 
    // by 3, we can't divide array in 3 parts of 
    // same sum. 
    if (total_sum%3 != 0) 
        return 0; 

    // Creating and filling suffix array 
    int suffix[n]; 
    suffix[n-1] = arr[n-1]; 
    for (int i=n-2; i>=0; i--) 
        suffix[i] = suffix[i+1] + arr[i]; 

    //<------Don't understand why we are doing things below------------->
    // Storing all indexes with suffix 
    // sum equal to total sum by 3. 
    vector <int> v; 
    for (int i=0; i<n; i++) 
        if (suffix[i] == total_sum/3) 
            v.push_back(i); 

    // Traversing the prefix array and 
    // doing binary search in above vector 
    for (int i=0; i<n; i++) 
    { 
        // We found a prefix with total_sum/3 
        if (prefix[i] == total_sum/3) 
        { 
            // Find first index in v[] which 
            // is greater than i+1. 
            int res = binarysearch(v, i+1); 

            if (res != -1) 
                count += v.size() - res; 
        } 
    } 

    return count; 
} 

// driver function 
int main() 
{ 
    int arr[] = {1 , 2 , 3 , 0 , 3}; 
    int n = sizeof(arr)/sizeof(arr[0]); 
    cout << CountContiguousParts(arr, n); 
    return 0; 
} 

我不遵循创建向量 v 背后的逻辑,该向量存储所有后缀总和等于 (total sum/3) 的索引,然后进行二分查找,返回大于 i+1 的第一个元素的索引。

对于给定的示例{1, 2, 3, 0, 3},前缀数组是{1, 3, 6, 6, 9},而后缀数组是{9, 8, 6, 3, 3}。所以按照上面的逻辑,对于前缀数组索引i=1处的3,我们在后缀数组索引i=3处找到3,并将v.size() - res添加到count,这就是我们的答案2 在这里。 但是,我不明白它的作用以及它如何为我们提供答案。

想的是前缀数组中索引 i=1 处的 3,我们应该计算后缀中以 i+1 开头的 3 的出现次数数组并返回该计数。

有人可以详细说明上面的代码是做什么的吗?

【问题讨论】:

标签: c++ algorithm logic


【解决方案1】:

您想将一个数组分成三部分,以便每个部分的总和与其他部分相等。假设我们的分区是P1P2P3。我们想要这种关系: P1=P2=P3=x

我们还有P1 + P2 + P3 = 3x = total sum of array

所以很明显,我们的数组之和必须是 3 的倍数,否则就不可能分割我们的数组以满足我们的要求。

 vector <int> v; 
for (int i=0; i<n; i++) 
    if (suffix[i] == total_sum/3) 
        v.push_back(i);

你同意P3 应该包含数组总和的三分之一吗?使用上面的代码,我们只存储候选索引。

  for (int i=0; i<n; i++) 
    { 
        // We found a prefix with total_sum/3 
        if (prefix[i] == total_sum/3) 
        { 
            // Find first index in v[] which 
            // is greater than i+1. 
            int res = binarysearch(v, i+1); 

            if (res != -1) 
                count += v.size() - res; 
        } 
    }

然后使用上面的代码我们也应用相同的逻辑首先我们计算其中前缀包含数组总和的三分之一让我们称之为i然后在v(看看第一个代码sn-p它包含什么) 我们搜索一个其值大于i + 1 的索引,如果我们能找到这样的索引,那么P2 也可以创建让我们称之为j(它是第一个更大的索引比i + 1)现在我们的分区看起来像这样:

[0 ..... i][i+1 ....j-1][j .... n]

现在下面的代码做了什么:

    if (res != -1) 
        count += v.size() - res; 

您是否同意jj 之后的所有索引,即j+1, j+2, j+3, ... , v.size()-1 它们也满足我们的要求?即

[0 ..... i] [i+1 ....v[j-1]] [v[j] .... n]

[0 ..... i] [i+1 ....v[j]]   [v[j+1] .... n]

[0 ..... i] [i+1 ....v[j + 1]] [v[j+2] .... n]
...
[0 ..... i] [i+1 ....v[v.size()-2]] [v[v.size()-1] .... n]

它们都是相等的分区(v 包含原始数组的索引,因此如果我们选择值并转到原始数组并从那里求和到末尾,它是原始数组总和的三分之一)。此代码仅计算所有有效状态。 并且算法一直持续到找到所有状态为止。

【讨论】:

  • 嗯...我明白这一点(因为我们找到了j,所以分区是可能的)..我不明白v.size() - res如何给我们count。你能详细说明一下吗?
  • 是的,这是有道理的。谢谢! :)
猜你喜欢
  • 2014-07-31
  • 1970-01-01
  • 1970-01-01
  • 2013-01-18
  • 1970-01-01
  • 1970-01-01
  • 2015-02-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多