% Albert Cao % Pizza Problem - Ordering pizza for the boys. % https://www.minizinc.org/doc-2.5.3/en/part_2_tutorial.html % Given: int: n; int: m; set of int: pizzas = 1..n; % n set of int: coupons = 1..m; % m array[pizzas] of int: price; % of each pizza array[coupons] of int: buy; % we must buy a certain number of pizzas to use each coupon array[coupons] of int: free; % each coupon we use lets us get some number of pizzas free int: costBound; % the amount of money we have to spend. % Find: var set of pizzas: paid; % set of pizzas we will pay for var set of coupons: used; % set of coupons we will use array[coupons, pizzas] of var bool: justifies; % holds if pizza p is one of the pizzas purchased to justify using coupon c array[coupons, pizzas] of var bool: usedFor; % holds if we are getting pizza p free by using coupon c (p = price, c = buy) var int: cost = sum(p in paid)(price[p]); % the final amount of money spent. % Constraint 1 - we pay for exactly the pizzas we don't get for free by using coupons. % For all pizzas p, p is in paid if and only if there is not a coupon c that was used to get p. % Defines the set of paid pizzas. constraint forall(p in pizzas) ( (p in paid) <-> not exists(c in used)(usedFor[c, p]) ); % Constraint 2 - Used is the set of coupons we use. % For all coupons c, c is in used if and only if there is a pizza p that was received by using c. % Defines the set of used coupons. constraint forall(c in coupons) ( (c in used) <-> exists(p in pizzas)(usedFor[c, p]) ); % Constraint 3 - Any coupon we used must be justified by enough paid pizzas. % For all coupons c, if c is in used, then the number of pizzas p that we used to justify applying c is at least as many as the number of pizzas c says we have to buy. constraint forall(c in coupons) ( (c in used) -> sum(p in pizzas)(justifies[c, p]) >= (buy[c]) ); % Constraint 4 - No coupon is used for too many pizzas. % For all coupons c, the number of pizzas we get for free by applying a coupon is at most the number of pizzas c says we're allowed to get free. constraint forall(c in coupons) ( sum(p in pizzas)(usedFor[c, p]) <= (free[c]) ); % Constraint 5 - Each free pizza costs at most as much as the cheapest pizza purchased to justify the coupon used. % For all coupons c, and every 2 pizzas p1, p2 (that are not identical), if we used c to justify getting p1 for free, and we bought p2 to justify applying c, % then the price of p1 must be at most the price of p2. constraint forall(c in coupons, p1, p2 in pizzas where p1 != p2) ( (usedFor[c, p1] /\ justifies[c, p2]) -> (price[p1] <= price[p2]) ); % Constraint 6 - We pay for all pizzas that justify using coupons. % For all coupons c and pizzas p, if p is used to justify applying c, then we must pay for p. constraint forall(c in coupons, p in pizzas) ( justifies[c, p] -> (p in paid) ); % Constraint 7 - The total cost is not too high. % The price of the sum over all pizzas p that are paid for is at most costBound. constraint sum(p in paid)(price[p]) <= costBound; % Constraint 8 - Justifies and UsedFor hold only of coupon-pizza pairs. % For all coupons c and pizzas p, if justifies(c, p) then c has to be a coupon and p has to be a pizza. % For all coupons c and pizzas p, if usedFor(c, p) then c has to be a coupon and p has to be a pizza. constraint forall(c in coupons, p in pizzas) ( (justifies[c, p] -> (1 <= c /\ c <= m /\ 1 <= p /\ p <= n)) /\ (usedFor[c, p] -> (1 <= c /\ c <= m /\ 1 <= p /\ p <= n)) ); % Bonus Constraint - For all coupons c and pizzas p, if c has been used to justify getting p, then it cannot be used again. % For all coupons c and pizzas p, if justifies(c, p) then there is not a coupon q not equal to c that was used to justify getting p. constraint forall(c in coupons, p in pizzas) ( (justifies[c,p] -> not exists(q in coupons where c != q)(justifies[q,p])) ); % Such that: we get all pizzas, cost is less than or equal to costBound, and coupons are used only if justified. solve minimize cost; % Print output. output ["cost(" ++ show(cost) ++ ")\n" ++ "costBound(" ++ show(costBound) ++ ")\n"];
n = 4; price = [10,5,20,15]; m = 2; buy = [1,2]; free = [1,1]; costBound = 50;