-1

I was trying to do this question:

Given three integers: GA, GB, and GC(which represent apples, oranges, and bananas respectively) & N lines each consisting of three integers: A, B, and C, which represent the amount of apples, oranges, and bananas in that food, respectively.

Check if it's possible to use only certain cartons such that the total apples, oranges, and bananas sum up to GA, Gb and GC respectively. For each available carton, we can only choose to buy it or not to buy it. He can't buy a certain carton more than once, and he can't buy a fractional amount of a carton.

Sample Test Case

IN

100 100 100
3
10 10 40
10 30 10
10 60 50

OUT

no

IN

100 100 100
5
40 70 30
30 10 40
20 20 50
10 50 90
40 10 20

OUT

yes

For this problem, I have written some code but have been getting segmentation faults only and a number of errors. Plus, my algorithm is quite bad. What I do is find all subsets of the apples array such that their sum is GA, then I check to see if any of those sets have oranges and bananas to add to GB and GC. But this idea is quite slow and very difficult to code...

I believe this is somewhat a variation of the knapsack problem and can be solved in a better complexity (atleast better than O(2^N)[My current complexity] ;P ). SO, what would be a better algorithm to solve this question, and also, see my current code at PasteBin(I havent put the code on stackoverflow because it is flawed, and moreover, I believe I'll have to start from scratch with it...)

Zeta
  • 103,620
  • 13
  • 194
  • 236
hell yeah
  • 17
  • 5
  • 2
    This is the well known knapsack problem. It is NP-hard, but a [dynamic programming](http://cse.unl.edu/~goddard/Courses/CSCE310J/Lectures/Lecture8-DynamicProgramming.pdf) approach is *pseudo-polynomial*. – Willem Van Onsem Jan 09 '15 at 05:05
  • @CommuSoft Ya, I know that, but it is somehow multidimensional in nature, I mean, we are supposed to check 3 things, how can we do it ? I mean, I don think there is any other way than checing all possible sets... – hell yeah Jan 09 '15 at 05:09
  • Cant someone help me ? – hell yeah Jan 09 '15 at 05:43
  • 1
    That doesn't really make things more complicated, simply store tuples of three where you would have stored one value previously. And eliminate from the moment one exceeds it. I don't know much about C++, is a *high level description* sufficient? – Willem Van Onsem Jan 09 '15 at 05:48
  • I provided an algorithm that (as you can see below) runs nicely on the test examples. I don't know much about the C++ library. I can implement it in C# howver... – Willem Van Onsem Jan 09 '15 at 06:11
  • @CommuSoft: Why would you add a completely unrelated language to this question? If OP wants a language agnostic answer, use that tag, but don't tag additional languages, especially if OP doesn't know them. – Zeta Jan 09 '15 at 09:06
  • @Zeta: as argued before, I provided an answer in *Haskell* (since it was more convenient than providing one in C++), as a result, the question becomes bi-lingual... – Willem Van Onsem Jan 09 '15 at 09:09
  • @CommuSoft: *"Hey, I want to do (something) in C++, but I'm having problems"* - *"sure, here is how to do it in Haskell/Python/ML/Ruby"*. _You_ used an unrelated language to answer the question (see OP's comment: "I've got no knowledge of Haskell, can you tell me what exactly you do?"). That doesn't make the original question bi-lingual (it's obviously some kind of homework exercise or similar). A proper tag would be language agnostic. – Zeta Jan 09 '15 at 09:14
  • @Zeta: as you can see above, I asked if it was good to provide a high-level description. Furthermore the OP *accepted* the answer. And no, it doesn't make the question bi-lingual, but if a user in the future wants such algorithm in haskell, he can find it. But no problem removing the tag. – Willem Van Onsem Jan 09 '15 at 09:17

3 Answers3

1

The segmentation faults are entirely your problem.

Knapsack is NP-complete, and so is this (assume input where A, B, C are always the same, and Ga = half the sum of the A's). I don't think anyone is asking you to solve NP-complete problems here.

Obviously you don't check all sets, but only those with sum A <= 100, sum B <= 100, sum C <=100.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • well it is *pseudo-polynomial*, one can adapt that algorithm to get a competitive one. Don't forget in the early days *Knapsack* was considered that safe they designed an encryption algorithm based on it. That turned out to be a huge mistake, now machines can solve such problems with hundreds of items easily. – Willem Van Onsem Jan 09 '15 at 06:01
1

Same situation as with this question.

This question is 2nd problem from Facebook Hackercup qualification round which is currently in progress (it will end 12th of January 12AM UTC).

It's not really fair to ask here solutions for the problems of active programming competitions.

Community
  • 1
  • 1
Jarlax
  • 1,586
  • 10
  • 20
  • This is indeed totally unfair. But little did I know before providing an answer. Since social networks are a total waste of time, I don't use them... – Willem Van Onsem Jan 10 '15 at 03:07
  • @CommuSoft It's not required to actively use FB to participate. Many just create account with first/last name and nothing else. And after the contest delete acc. [Facebook Hackercup](https://www.facebook.com/hackercup) (similarly to [Google CodeJam](https://code.google.com/codejam)) was initially created to hire smart people and to make programming more popular. But despite of that - participating is super fun. – Jarlax Jan 10 '15 at 11:22
0

This is a variant of the 0-1 knapsack problem. This problem is NP-hard, so there is not much hope to find a solution in polynomial time, but there exists a solution in pseudo-polynomial time which makes this problem rather easy (in the world of complex problems).

The algorithm works as follows:

  1. Start with a collection (for instance a set) containing the tuple <0,0,0>.
  2. For each carton <a',b',c'>: iterate over the all tuples <a,b,c> in the collection and add <a+a',b+b',c+c'> to the collection, ensure that duplicates are not added. Don't add tuples where one or more elements have exceeded the corresponding target value.
  3. If the given collection contains the target values after the algorithm, print "yes", otherwise "no".
  4. Optionally but strongly advisable lower-bound elimination: you can also perform lookaheads and for instance eliminate all values that will never reach the given target anymore (say you can at most add 20 apples, then all values less than 80 apples can be eleminated).

    Concept 1 (Lowerbound): Since you add values of tuples together, you now that if there are tuples <a0,a1,a2> and <b0,b1,b2> left, adding these will at most increase a tuple with <a0+b0,a1+b1,a2+b2>. Now say the target is <t0,t1,t2> then you can safely eliminate a tuple <q0,q1,q2> if q0+a0+b0 < t0 (generalize to other tuple elements), since even if you can add the last tuples, it will never reach the required values. The lower bound is thus <t0-a0-b0,t1-a1-b1,t2-a2-b2>. You can generalize this for n tuples.

So first you add up all provided tuples together (for the second instance, that's <140,160,230>) and than subtract that from the target (the result is thus: <-40,-60,-130>). Each iteration, the lower bound is increased with that carton, so after the first iteration, the result for the second example is (<-40+40,-60+70,-130+30> or <0,10,-100>).

The time complexity is however O(ta^3 tb^3 tc^3) with ta, tb and tc the target values.

Example 1 (high level on the two given testcases):

INPUT

100 100 100
3
10 10 40
10 30 10
10 60 50

The set starts with {<0,0,0>}, after each iteration we get:

  1. {<0,0,0>};
  2. {<0,0,0>,<10,10,40>};
  3. {<0,0,0>,<10,10,40>,<10,30,10>,<20,40,50>}; and
  4. {<0,0,0>,<10,10,40>,<10,30,10>,<20,40,50>,<10,60,50>,<10,60,50>,<20,70,90>,<30,100,100>}, thus fail.

With underbound-elimination:

  1. {<0,0,0>}, lowerbound <100-30,100-100,100-100>=<70,0,0> thus eliminate <0,0,0>.
  2. {} thus print "no".

Example 2

INPUT

100 100 100
5
40 70 30
30 10 40
20 20 50
10 50 90
40 10 20

With lower-bound elimination:

  1. {<0,0,0>} lower bound: <-40,-60,-130> thus ok.
  2. {<0,0,0>,<40,70,30>} lower bound: <0,10,-100> (eliminate <0,0,0> because second conflicts).
  3. {<40,70,30>,<70,80,70>} lower bound: <30,20,-60> (no elimination).
  4. {<40,70,30>,<70,80,70>,<60,90,80>,<90,100,120>} lower bound: <50,40,-10> (eliminate <40,70,30>) upper eliminate <90,100,120>.
  5. {<70,80,70>,<60,90,80>,<80,130,160>,<70,140,170>} lower bound: <60,90,80> (eliminate <70,80,70>) upper eliminate <80,130,160> and <70,140,170>.
  6. {<60,90,80>,<100,100,100>} lower bound: <100,100,100> (eliminate <60,90,80>).
  7. {<100,100,100>} thus "yes".

Haskell program

I've implemented a (not that efficient, but proof of concept) Haskell program that does the trick for an arbitrary tuple-length:

import qualified Data.Set as Set

tupleSize :: Int
tupleSize = 3

group :: Int -> [a] -> [[a]]
group _ [] = []
group n l = take n l : group n (drop n l)

empty :: Int -> Set.Set [Int]
empty n = Set.fromList [replicate n 0]

solve :: [Int] -> [[Int]] -> Bool
solve t qs = Set.member t $ mix t (lowerBound t qs) qs $ empty $ length t

lowerBound :: [Int] -> [[Int]] -> [Int]
lowerBound = foldl (zipWith (-))

lowerCheck :: [Int] -> [Int] -> Bool
lowerCheck l x = and $ zipWith (<=) l x

targetCheck :: [Int] -> [Int] -> Bool
targetCheck t x = and $ zipWith (>=) t x

takeout :: Int -> [a] -> [a]
takeout _ [] = []
takeout i (h:hs) | i == 0 = hs
                 | otherwise = h : takeout (i-1) hs

mix :: [Int] -> [Int] -> [[Int]] -> Set.Set [Int] -> Set.Set [Int]
mix _ _ [] s = s
mix t l (q:qs) s = mix t (zipWith(+) l q) qs $ Set.filter (lowerCheck l) $ Set.union s $ Set.filter (targetCheck t) $ Set.map (zipWith (+) q) s

reply :: Bool -> String
reply True = "yes"
reply False = "no"

main = interact $ \x -> let tuples = group tupleSize $ takeout tupleSize $ map read (words x) in reply $ solve (head tuples) (tail tuples)

You can compile an run it using:

ghc file.hs
./file < input

Conclusion: Although the worst-case behavior can be hard, the second example shows that the problem can be solve efficiently for some cases.

Willem Van Onsem
  • 443,496
  • 30
  • 428
  • 555
  • I do not undertstand what you mean by lower bound elimination. Can you explain it more deeply ? – hell yeah Jan 09 '15 at 07:23
  • @hellyeah: one minute, I added a *Haskell* program that does the trick (and can even be extended to tuples with 4, 5,...). It however requires to close the stream before it says the result. – Willem Van Onsem Jan 09 '15 at 07:28
  • I've got no knowledge of Haskell, can you tell me what exactly you do ? – hell yeah Jan 09 '15 at 07:50
  • Moreover, I totally dont understand what you do when you say "lower bound elimination" – hell yeah Jan 09 '15 at 07:50
  • @hellyeah: you eliminate all items where there is at least one element less than the lower bound. In the second example in the second round `<0,10,-100>` is the lower bound and thus we eliminate `<0,0,0>` because the second item is too low. You thus remove items from your set, to make the life of the CPU nicer. – Willem Van Onsem Jan 09 '15 at 07:51
  • @CommuSoft: Just to be clear: your explanation of the dynamical algorithm is fine. But the Haskell program isn't needed, especially in a question that was originally [tag:c++] only. – Zeta Jan 09 '15 at 09:18