简体   繁体   中英

Find all the numbers that can be represented as sum of three distinct elements of an array

You have an array A containing N distinct positive integers. You also have an empty set S. Your task to add as many integers as possible to set S. An integer can be added to set S if it can be represented as sum of three distinct elements of array A, and is already not present in the set S.

Example - If N = 5, and array contains 2, 4, 6, 8, 9. Then the set contains 9 elements --> 12, 14, 15, 16, 17, 18, 19, 21, 23

The O(n^3) approach is easy, but will take a lot of time for large arrays. Can someone suggest an efficient way to solve this?

Not sure if my solution is actually faster than the brute force approach (expecting giggles and downvotes). But, my solution uses dynamic programming... I think :)

The basic idea is that the solution for the problem (N,1) is trivial. The sum of each 1 tuple of values in the input array is just the input array.

And the solution of the problem (N,M) can use the solution of the problem (N,M-1), by simply mapping over all elements in the (N,M-1) solution, which looks like Map [Int] Int . Now, all indices not yet used in a specific element of the (N,M-1) solution need to be combined with each key in the (N,M-1) solution and the sum of the values of the (N,M-1) solution + the value of the unused index creates an element of the (N,M) solution.

And here we have our recursion. And our memoization of the "simpler" results (N,M-1) solution which we use for our solving of the (N,M). And herewith, we did a dynamic programming approach. (recursion + memoization = dynamic programming).

In the language I currently try to learn (Haskell), I came up with this implementation (which is probably considered ugly by many ;)

import Data.Array
import Data.List
import qualified Data.Map.Strict as Map


type Lut = Map.Map [Int] Int

foo :: Array Int Int -> Int -> Int -> Lut
-- This is our trivial base case. Create a map for the (N,1) case.
-- "input ! i" means what other languages would write as input[i].
foo input n 1 = Map.fromList [([i],input ! i) | i <- [1..n]]
-- This is the general case, which uses recursion.
foo input n m =
    (Map.fromList . fixKeys . concat . fmap sums) (Map.keys lut)
    where 
        lut :: Lut
        -- Here, the recursion happens. (lut stands for LookUp Table)
        lut = foo input n (m-1)
        all = [1..n]
        sums k =
            -- Here we use our (N,M-1) data to create the output
            -- for this recursion step. 
            -- (forall unused element indices do input[i] + lut[k])
            fmap (\(i,k) -> (i:k,input ! i + query k) ) unused
            where 
                -- The \\ means: difference. 
                -- All indices minus the indices used in a key of a (N,M-1) 
                -- element.
                unused = fmap (\i -> (i,k)) (all \\ k)

        query k =
            case Map.lookup k lut of
                Just v -> v
                Nothing -> error "key cannot not be in the map!"
        -- Remove duplicates (e.g. [1,2,3] and [1,3,2]) by sorting them.
        fixKeys l =
            fmap (\(k,v) -> (sort k,v)) l




to1BasedArray l = listArray (1,length l) l

raw = [2, 4, 6, 8, 9]
input = to1BasedArray raw 

output = foo input (length raw) 3

Upon popular demand, here the C++ version of this ... thing :)

#include "stdafx.h"
#include <vector>
#include <map>
#include <algorithm> // sort()
#include <iostream>

typedef std::vector<int32_t> Data;
typedef std::vector<size_t> Key;
typedef std::map<Key, int32_t> Lut;

auto contains(const Key& indices, size_t index) -> bool
{
    for (auto x : indices)
    {
        if (x == index)
            return true;
    }
    return false;
}

auto unused(size_t n, const Key& k) -> Key
{
    Key result;
    result.reserve(n);
    for (size_t i = 1; i <= n; i++)
    {
        if (!contains(k, i))
        {
            result.push_back(i);
        }
    }
    return result;
}

// 'input' is a vector we use as 1-based (not 0 based) array.
auto foo(const Data& input, size_t n, size_t m) -> Lut
{
    Lut result;
    switch (m)
    {
    case 1:
        for (size_t i = 1; i <= n; i++)
        {
            Key k = { i };
            result[k] = input[i];
        }
        break;
    default:
        {
            Lut lut = foo(input, n, m - 1);
            for (const auto& kv : lut)
            {
                auto uns = unused(n, kv.first);
                for (auto i : uns)
                {
                    auto nk = Key(kv.first.begin(), kv.first.end());
                    nk.push_back(i);
                    std::sort(nk.begin(), nk.end());
                    result[nk] = kv.second + input[i];
                }
            }
        }
        break;
    }
    return result;
}

std::ostream& operator<<(std::ostream& os, const Key& values)
{
    bool first = true;
    os << "[";
    for (const auto v : values)
    {
        if (first)
        {
            os << v;
            first = false;
        }
        else
        {
            os << ", " << v;
        }
    }
    os << "]";
    return os;
}

int main()
{
    // leading 0 because it is a 1 based array.
    Data data = { 0, 2, 4, 6, 8, 9 }; 
    Lut result = foo(data, 5, 3);
    for (auto kv : result)
    {
        std::cout << kv.first << " = " << kv.second << std::endl;
    }
    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM