/* Different formulation: each card allocates itself */ param K, integer, > 0; /* number of cards */ param P, integer, > 0; /* number of platters */ param M, integer, > 0; /* maximum printing of a single platter */ set card_set := 1..K; /* set of cards */ set platter_set := 1..P; /* set of platters */ param card_demand{i in card_set}, >= 0, integer <= M; /* demand for cards */ param cost_platter, >= 0; /* fixed-charge cost per platter */ param cost_overproduced_card, >= 0; /* Cost for a single overproduced card */ param cards_per_platter, integer, > 0; /* number of cards per platter */ var card_to_platter_count{i in card_set, j in platter_set}, >= 0, <= M; var card_to_platter_flag{i in card_set, j in platter_set}, binary; var platter_print{j in platter_set}, >= 0, <= M; var platter_used{j in platter_set}, binary; /* indicator variable: platter j used */ var card_overproduction{i in card_set}, >= 0; /* card overproduction slacks */ /* Constraint 1: the maximum-volume card decides the platter_print */ s.t. maximum_volume_induction{i in card_set, j in platter_set}: card_to_platter_count[i,j] <= platter_print[j]; /* Constraint 2: keep track of used platters */ s.t. platter_use_constraint{i in card_set, j in platter_set}: card_to_platter_flag[i,j] <= platter_used[j]; /* Constraint 3: count cards per platter */ s.t. card_count_constraint{i in card_set, j in platter_set}: card_to_platter_count[i,j] <= M*card_to_platter_flag[i,j]; /* Constraint 4: respect platter capacity */ s.t. platter_capacity_constraint{j in platter_set}: sum{i in card_set} card_to_platter_flag[i,j] <= cards_per_platter; /* Constraint 5: enforce production of demand */ s.t. demand_satisfaction{i in card_set}: sum{j in platter_set} card_to_platter_count[i,j] >= card_demand[i]; s.t. fix_card_to_platter_count_constraint{j in platter_set}: sum{i in card_set} card_to_platter_count[i,j] = cards_per_platter*platter_print[j]; /* Calculate overproduction */ s.t. calculate_overproduction{i in card_set}: sum{j in platter_set} card_to_platter_count[i,j] - card_overproduction[i] = card_demand[i]; /* Objective: minimize used platters and overproduced cards */ minimize cost: sum{i in card_set} cost_overproduced_card*card_overproduction[i] + sum{j in platter_set} cost_platter*platter_used[j]; data; param cost_platter := 50.0; param cost_overproduced_card := 0.04; param cards_per_platter := 8; param K := 30; /* Number of cards */ param P := 12; /* Number of platters */ param M := 11500; param card_demand := 1 3200, 2 2400, 3 7500, 4 6800, 5 1200, 6 11500, 7 2800, 8 2100, 9 700, 10 1900, 11 5000, 12 2100, 13 100, 14 750, 15 4300, 16 4200, 17 5950, 18 8600, 19 1340, 20 1780, 21 6250, 22 1050, 23 4670, 24 1400, 25 1400, 26 6100, 27 2700, 28 1600, 29 2000, 30 1200; option cbc_options "passF=50 reduce=on local=on rins=on lift=on residual=on two=on log=2"; option solver cbc; solve; /* Show results */ printf "\nCard to platter allocation:\n"; for {i in card_set} { printf "%3d %6d :", i, card_demand[i]; for {j in platter_set} { printf " %d(%f)", card_to_platter_flag[i,j], card_to_platter_count[i,j]; } printf " : total %d, %d too much (%f EUR)\n", sum{j in platter_set} card_to_platter_count[i,j], card_overproduction[i], card_overproduction[i]*cost_overproduced_card; } /* Show platter allocations */ printf "\nPlatter print counts:\n"; for {j in platter_set} { printf "%3d: %6d\n", j, platter_print[j]; } printf "\nTotal platters used: %d\n", sum{j in platter_set} platter_used[j]; printf "\nTotal cost: %f EUR\n", sum{j in platter_set} cost_platter*platter_used[j] + sum{i in card_set} cost_overproduced_card*card_overproduction[i]; end;