Unable to solve a homework (ACM-training)
I have no idea how to solve this: http://acm.sgu.ru/problem.php?contest=0&probl开发者_开发知识库em=311
Please help me with this one
I know it can be solved using segment tree but I don't know how
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.
Handling an arrival of pieces is a straightforward O(log n)-time descent in the segment tree.
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.
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).
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.
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
精彩评论