0

I have no idea how to solve this: http://acm.sgu.ru/problem.php?contest=0&problem=311

Please help me with this one

I know it can be solved using segment tree but I don't know how

OmG
  • 18,337
  • 10
  • 57
  • 90
  • 2
    What have you tried so far? It's actually difficult to help ACM problems because mere solution wont teach you something. You have to get the background details, such as Dynamic programming first. – Shamim Hafiz - MSFT Feb 28 '11 at 12:17
  • 1
    This looks rather straightforward, so what have you tried so far? – Justin Morgan Feb 28 '11 at 12:18
  • I have nothing better than stefaanv's answer which for sure gets TLE. –  Mar 02 '11 at 06:23

4 Answers4

4
  1. Read all of the prices and set up a segment tree. For each segment, store the number and total cost of pieces whose prices lie in that segment. That's most of the problem, and the rest of this answer will be rather vague in hopes that you learn something.

  2. Handling an arrival of pieces is a straightforward O(log n)-time descent in the segment tree.

  3. Handling a purchase request is also an O(log n)-time descent followed by an update if a sale was made. The update may traverse a large amount of the segment tree and is fast only in an amortized sense – an interval should be entered if and only if there are pieces in that price range, and the running time of their removal should be charged to the arrival.

user635541
  • 1,214
  • 6
  • 7
  • +1. Note that prices are integers, $1M and under. So your segment tree can be implicit (an array for each level of the tree), which makes the implementation much easier. – Tom Sirgedas Mar 09 '11 at 20:36
2

Loop per line:

  • interprete line command with parameters (ARRIVE/BUY)
  • update model (number icecreams per price), model: std::map price to quantity.
  • in case of BUY, output whether enough icecreams can be sold, selling cheapest icecreams first (first in map).
stefaanv
  • 14,072
  • 2
  • 31
  • 53
  • This would just get TLE. See the limits please –  Mar 02 '11 at 06:22
  • I'm not familiar with the acronym TLE. What does it stand for? I don't see how the limits have an influence on the basic algorithm, but how 3 seconds should be reached is beyond the scope of a simple exercise... – stefaanv Mar 03 '11 at 08:43
  • OK! So I could easily code a n^2 algorithm. This is not solving the problem –  Mar 06 '11 at 18:08
  • & TLE means "time limit exceed" The only problem is solving it in the time, not how to let it take an hour to answer :P –  Mar 06 '11 at 18:15
  • I already figured that 3 seconds to solve it would be tight. But what does 3 seconds mean on a handheld ARM or on a fullblown server... However the algorithm is not n^2 as it is just updating a map and iterating over the first needed elements based on the price (and as user said, only updating when the buy is valid). – stefaanv Mar 07 '11 at 07:34
  • Mind you that the TLE thing is not very clear from your question and the provided link. Mentioning that you already have an n^2 algorithm, but needed something faster, would have given you more (better) answers. – stefaanv Mar 07 '11 at 07:37
  • It is n^2, perhaps even n^2 log n, if most of the customers attempt and fail to buy most of the pieces. – user635541 Mar 07 '11 at 12:30
0

I have to say that I am not sold on segment trees as the best solution to this problem. Whether they are the "best" data structure seems to depend on the ratio of ARRIVE to BUY requests, because each time you make a sale, there is going to be quite a lot of work to update your segment tree after the removal of a segment.

What if you stored your inventory as a linked-list and each node contained the number of items for sale at a particular per-unit cost. This would make the cost of inserting and removing inventory much lower. To check whether you can make a sale you have to iterate through a while loop accumulating cost and quantity until you meet the goal or exceed the cost. The advantage here is that, if you make a sale, then the node you stop on is now the lowest cost of piece of inventory with which you want to start your next search. If you are working in a garbage collecting language you can just change the reference to the head of your list to the node you stopped on which would make for succinct code.

On an arrival of new inventory with a unit cost insertion is at worst O(n) where n is the # of arrivals you have. I think in the real world this wouldn't be a bad system because a real business would expect a high number of sales (happy) to non-sales (unhappy). Where this approach would perform poorly is if there were many people that wanted to buy almost your whole inventory but were just a little short on money to accomplish it.

Samsdram
  • 1,615
  • 15
  • 18
  • "a lot of work to update" - possibly, but the average work is small. "linked list" - no way. Either you keep it sorted and arrives are linear-time, or you don't and buys are linear-time. In both cases, failed sales are worst-case linear. "real world" - this isn't the real world; it's an exercise in data structures. – userOVER9000 Mar 09 '11 at 21:37
0

I wrote this out of my head just now (15min). Only testet with 100k repeated queries from the example, and with that it takes 1s.

please comment cause it might be bullshit:

#!/usr/bin/python
import sys

class store:
    def __init__(self):
        self.sorted = [] # list of tuples (cost, amount) - sorted by cost
        # btw: sorting of tuples is by its elements, so first by element 0
        # that means it is sorted by cost, cheapest first
        self.count = 0 # the global amount of ice in store
        self.mergemap = {} # key = cost, value = tuple of (cost, amount)
        # the mergemap prevents the creation of multiple tuples per priceclass

    def request(self, n, money):
        if self.count < n:
            # if we have less ice in store than was requested -> unhappy
            return False
        pcsleft = n # we count down

        # loop through each priceclass as x
        for x in self.sorted:
            # x[0] is cost, x[1] is the amount of ice of that price in store
            if x[1] < pcsleft and x[0]*x[1] <= money:
                # we found cheap ice, but there is not enough of it
                pcsleft -= x[1]
                money -= x[0]*x[1]
            elif x[1] >= pcsleft and x[0]*pcsleft <= money:
                # theres enough cheap ice, next iteration will not occour
                pcsleft = 0
                money -= x[0]*pcsleft
            else:
                return False # the cheapest ice is too expensive
            if pcsleft == 0 and money >= 0:
                break # happy - break because for-each loop, not while
            if money < 0: # just for kicks, should never happen
                print "error"
                sys.exit(1)
        # when we have cheap enough ice, we remove ice from storage
        # we iterate through the priceclasses like before
        # but when we remove ice from one category, either the loop ends
        # or we completly deplete it  so it can be removed
        while n > 0: # subtract the bought count
            x = self.sorted[0]
            if x[1] > n: # the first priceclass has enough ice
                x[1] -= n
                self.count -= n
                return True # were happy
            elif x[1] == n:
                del(self.mergemap[x[0]]) # remove from mergemap
                self.sorted = self.sorted[1:] # completly remove priceclass
                self.count -= n
                return True
            elif x[1] < n: # 
                n -= x[1]
                self.count -= x[1]
                del(self.mergemap[x[0]])
                self.sorted = self.sorted[1:]

        return True

    def arrive(self, n, cost):
        if cost not in self.mergemap: # we dont have ice in that priceclass
            # create a new tuple, actually list, cause tuples are immutable
            x = [cost, n]
            self.sorted.append(x)
            # resort, should be fast, cause its almost entirely sorted,
            # and python has sorting magic :)
            self.sorted.sort()
            self.mergemap[cost] = x # insert into mergemap
        else:
            # just update the tuple, via its reference in the mergemap
            self.mergemap[cost][1]+=n
            self.count
        self.count += n # keep count correct
        # O(n*logn)

if __name__=='__main__':
    s = store()
    i = 0
    # we read from stdin
    for line in sys.stdin:
        #print line.strip()+" --> ",
        req, count, cost = line.split(' ') # not error tolerant
        if req == 'ARRIVE':
            s.arrive(int(count), int(cost))
        elif req == 'BUY':
            if s.request(int(count), int(cost)):
                print 'HAPPY '+str(i)
            else:
                print 'UNHAPPY '+str(i)
        i+=1
    print s.sorted # print out the left over ice

EDIT: added comments

sleeplessnerd
  • 21,853
  • 1
  • 25
  • 29
  • The worst-case is quadratic. It would be O(n^2 log n) except for Timsort. – userOVER9000 Mar 09 '11 at 21:40
  • you could 'insert' the new element into the middle, and shift the rest, thats O(logn+n), but you would have to preallocate a list and expand it on demand, that would look ugly. :) - as you mentioned timsort is awesome. I also didnt want to go too much into detail, cause this audience (also the questioneer) is supposed to know about the best way to insert an item into a sorted list :) – sleeplessnerd Mar 09 '11 at 22:05
  • Well i think i know how you could use a segmented tree to solve this: every priceclass gets an interval, the interval begins with the sum of the prices of the ice units in the lower priceclasses +1*(price of this priceclass) and ends with again the sum of the prices of all lower priceclasses +n*(price of this class). So you can lookup your budget, and can find out IF you can be happy, and how. - But I only thought about this for a few seconds. – sleeplessnerd Mar 09 '11 at 22:19