From 2c5aed9216ed6277c6e42aee383b2b72a568e61d Mon Sep 17 00:00:00 2001 From: Oleksandr Petrykin Date: Fri, 4 Nov 2022 13:34:01 +0200 Subject: [PATCH 1/4] Problem 1 --- .gitignore | 1 + cmd/problem1/main.go | 11 +++ cmd/problem2/main.go | 5 ++ input.csv => data/input.csv | 1 + partners.csv => data/partners.csv | 2 +- delivery/entities.go | 52 +++++++++++++ delivery/repository.go | 21 ++++++ delivery/service.go | 120 ++++++++++++++++++++++++++++++ file/util.go | 39 ++++++++++ go.mod | 5 ++ go.sum | 2 + output1.csv | 4 - output2.csv | 4 - 13 files changed, 258 insertions(+), 9 deletions(-) create mode 100644 .gitignore create mode 100644 cmd/problem1/main.go create mode 100644 cmd/problem2/main.go rename input.csv => data/input.csv (57%) rename partners.csv => data/partners.csv (91%) create mode 100644 delivery/entities.go create mode 100644 delivery/repository.go create mode 100644 delivery/service.go create mode 100644 file/util.go create mode 100644 go.mod create mode 100644 go.sum delete mode 100644 output1.csv delete mode 100644 output2.csv diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..723ef36 --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +.idea \ No newline at end of file diff --git a/cmd/problem1/main.go b/cmd/problem1/main.go new file mode 100644 index 0000000..eab6c13 --- /dev/null +++ b/cmd/problem1/main.go @@ -0,0 +1,11 @@ +package main + +import "github.com/challenge2019/delivery" + +func main() { + d := delivery.NewDeliveryService(&delivery.Repository{}) + _, err := d.FindMinCostPartners("./data/input.csv", "./data/partners.csv") + if err != nil { + panic(err) + } +} diff --git a/cmd/problem2/main.go b/cmd/problem2/main.go new file mode 100644 index 0000000..7905807 --- /dev/null +++ b/cmd/problem2/main.go @@ -0,0 +1,5 @@ +package main + +func main() { + +} diff --git a/input.csv b/data/input.csv similarity index 57% rename from input.csv rename to data/input.csv index 3f1c8fa..4131341 100644 --- a/input.csv +++ b/data/input.csv @@ -1,3 +1,4 @@ +delivery_id,amount,theatre_id D1,150,T1 D2,325,T2 D3,510,T1 diff --git a/partners.csv b/data/partners.csv similarity index 91% rename from partners.csv rename to data/partners.csv index 3ea8c59..e00d34e 100644 --- a/partners.csv +++ b/data/partners.csv @@ -1,4 +1,4 @@ -Theatre,Size Slab (in GB),Minimum cost,Cost Per GB,Partner ID +theatre_id,slab,min_cost,cost_gb,partner_id T1 ,0-100 ,1500 ,20 ,P1 T1 ,100-200 ,2000 ,13 ,P1 T1 ,200-300 ,2500 ,12 ,P1 diff --git a/delivery/entities.go b/delivery/entities.go new file mode 100644 index 0000000..6f20a4e --- /dev/null +++ b/delivery/entities.go @@ -0,0 +1,52 @@ +package delivery + +import ( + "strconv" + "strings" +) + +type Input struct { + DeliveryID TrimString `csv:"delivery_id"` + Amount int `csv:"amount"` + TheatreID TrimString `csv:"theatre_id"` +} + +type Output struct { + DeliveryID TrimString `csv:"delivery_id"` + IsPossible bool `csv:"is_possible"` + PartnerID TrimString `csv:"partner_id"` + Cost int `csv:"cost"` +} + +type Partner struct { + TheatreID TrimString `csv:"theatre_id"` + Slab Slab `csv:"slab"` + MinCost int `csv:"min_cost"` + CostPerGB int `csv:"cost_gb"` + PartnerID TrimString `csv:"partner_id"` +} + +type Slab struct { + MinSlab int + MaxSlab int +} + +type TrimString string + +func (s *TrimString) UnmarshalCSV(csv string) (err error) { + *s = TrimString(strings.TrimSpace(csv)) + return +} + +func (s *Slab) UnmarshalCSV(csv string) (err error) { + ss := strings.Split(strings.TrimSpace(csv), "-") + s.MinSlab, err = strconv.Atoi(ss[0]) + if err != nil { + return err + } + s.MaxSlab, err = strconv.Atoi(ss[1]) + if err != nil { + return err + } + return nil +} diff --git a/delivery/repository.go b/delivery/repository.go new file mode 100644 index 0000000..37340e0 --- /dev/null +++ b/delivery/repository.go @@ -0,0 +1,21 @@ +package delivery + +import "github.com/challenge2019/file" + +type Repository struct{} + +func (r *Repository) FetchDeliveries(c chan<- error, fileName string, deliveries *[]Input) { + file.ReadAsync(c, fileName, deliveries) +} + +func (r *Repository) FetchPartners(c chan<- error, fileName string, partners *[]Partner) { + file.ReadAsync(c, fileName, partners) +} + +func (r *Repository) SaveDeliveriesOutput(fileName string, out *[]Output) error { + err := file.Write(fileName, out) + if err != nil { + return err + } + return nil +} diff --git a/delivery/service.go b/delivery/service.go new file mode 100644 index 0000000..b03be6f --- /dev/null +++ b/delivery/service.go @@ -0,0 +1,120 @@ +package delivery + +import ( + "sync" +) + +type Service struct { + Repo *Repository +} + +func NewDeliveryService(r *Repository) *Service { + return &Service{Repo: r} +} + +func fanOut(partners []Partner, out ...chan Partner) { + go func() { + for _, p := range partners { + for _, c := range out { + c <- p + } + } + + for _, c := range out { + close(c) + } + }() +} + +func merge(in ...chan Output) chan Output { + wg := sync.WaitGroup{} + out := make(chan Output, len(in)) + wg.Add(len(in)) + + for _, c := range in { + go func(ch chan Output) { + defer wg.Done() + for t := range ch { + out <- t + } + }(c) + } + + go func() { + wg.Wait() + close(out) + }() + + return out +} + +func (s *Service) checkPartner(in <-chan Partner, d Input) chan Output { + out := make(chan Output) + go func() { + for p := range in { + out <- func(p Partner) Output { + if d.TheatreID == p.TheatreID && d.Amount >= p.Slab.MinSlab && d.Amount <= p.Slab.MaxSlab { + cost := d.Amount * p.CostPerGB + if cost < p.MinCost { + cost = p.MinCost + } + return Output{DeliveryID: d.DeliveryID, IsPossible: true, Cost: cost, PartnerID: p.PartnerID} + } + return Output{DeliveryID: d.DeliveryID, IsPossible: false} + }(p) + } + close(out) + }() + + return out +} + +func (s *Service) FindMinCostPartners(deliveryFile, partnerFile string) ([]Output, error) { + var ( + deliveries []Input + partners []Partner + ) + + filesCount := 2 + errors := make(chan error, filesCount) + + go s.Repo.FetchDeliveries(errors, deliveryFile, &deliveries) + go s.Repo.FetchPartners(errors, partnerFile, &partners) + + for i := 0; i < filesCount; i++ { + if err := <-errors; err != nil { + return nil, err + } + } + + size := len(deliveries) + out := make([]chan Output, 0, size) + outMap := make(map[TrimString]Output) + result := make([]Output, 0, size) + deliveryChannels := make([]chan Partner, size, size) + + for i := range deliveries { + c := make(chan Partner) + deliveryChannels[i] = c + out = append(out, s.checkPartner(c, deliveries[i])) + } + + fanOut(partners, deliveryChannels...) + + for o := range merge(out...) { + if v, ok := outMap[o.DeliveryID]; !ok || (o.IsPossible && !v.IsPossible) || (o.IsPossible && o.Cost < v.Cost) { + outMap[o.DeliveryID] = o + } + } + + for _, o := range outMap { + result = append(result, o) + } + + err := s.Repo.SaveDeliveriesOutput("./data/output.csv", &result) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/file/util.go b/file/util.go new file mode 100644 index 0000000..c573230 --- /dev/null +++ b/file/util.go @@ -0,0 +1,39 @@ +package file + +import ( + "github.com/gocarina/gocsv" + "os" +) + +func ReadAsync(c chan<- error, fileName string, out interface{}) { + file, err := os.Open(fileName) + if err != nil { + c <- err + return + } + + defer file.Close() + + if err := gocsv.UnmarshalFile(file, out); err != nil { + c <- err + return + } + + c <- nil + return +} + +func Write(fileName string, data interface{}) (err error) { + err = os.Remove(fileName) + if err != nil && !os.IsNotExist(err) { + return + } + file, _ := os.Create(fileName) + defer file.Close() + + err = gocsv.MarshalFile(data, file) + if err != nil { + return + } + return +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..dda3591 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/challenge2019 + +go 1.19 + +require github.com/gocarina/gocsv v0.0.0-20220927221512-ad3251f9fa25 // indirect diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..2f542f9 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/gocarina/gocsv v0.0.0-20220927221512-ad3251f9fa25 h1:wxgEEZvsnOTrDO2npSSKUMDx5IykfoGmro+/Vjc1BQ8= +github.com/gocarina/gocsv v0.0.0-20220927221512-ad3251f9fa25/go.mod h1:5YoVOkjYAQumqlV356Hj3xeYh4BdZuLE0/nRkf2NKkI= diff --git a/output1.csv b/output1.csv deleted file mode 100644 index 7c7b275..0000000 --- a/output1.csv +++ /dev/null @@ -1,4 +0,0 @@ -D1,true ,P1,2000 -D2,true ,P1,3250 -D3,true ,P3,15300 -D4,false,"","" diff --git a/output2.csv b/output2.csv deleted file mode 100644 index adcd15b..0000000 --- a/output2.csv +++ /dev/null @@ -1,4 +0,0 @@ -D1,true ,P2,3000 -D2,true ,P1,3250 -D3,true ,P3,15300 -D4,false,"","" From 212d129280089ef28ae6ab4f60757c75c42952c5 Mon Sep 17 00:00:00 2001 From: Oleksandr Petrykin Date: Fri, 4 Nov 2022 13:48:21 +0200 Subject: [PATCH 2/4] Problem 1 --- delivery/service.go | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/delivery/service.go b/delivery/service.go index b03be6f..df0c3b6 100644 --- a/delivery/service.go +++ b/delivery/service.go @@ -50,19 +50,18 @@ func merge(in ...chan Output) chan Output { func (s *Service) checkPartner(in <-chan Partner, d Input) chan Output { out := make(chan Output) + min := Output{DeliveryID: d.DeliveryID, IsPossible: false} go func() { for p := range in { - out <- func(p Partner) Output { - if d.TheatreID == p.TheatreID && d.Amount >= p.Slab.MinSlab && d.Amount <= p.Slab.MaxSlab { - cost := d.Amount * p.CostPerGB - if cost < p.MinCost { - cost = p.MinCost - } - return Output{DeliveryID: d.DeliveryID, IsPossible: true, Cost: cost, PartnerID: p.PartnerID} + if d.TheatreID == p.TheatreID && d.Amount >= p.Slab.MinSlab && d.Amount <= p.Slab.MaxSlab { + cost := d.Amount * p.CostPerGB + if cost < p.MinCost { + cost = p.MinCost } - return Output{DeliveryID: d.DeliveryID, IsPossible: false} - }(p) + min = Output{DeliveryID: d.DeliveryID, IsPossible: true, Cost: cost, PartnerID: p.PartnerID} + } } + out <- min close(out) }() @@ -89,7 +88,6 @@ func (s *Service) FindMinCostPartners(deliveryFile, partnerFile string) ([]Outpu size := len(deliveries) out := make([]chan Output, 0, size) - outMap := make(map[TrimString]Output) result := make([]Output, 0, size) deliveryChannels := make([]chan Partner, size, size) @@ -102,12 +100,6 @@ func (s *Service) FindMinCostPartners(deliveryFile, partnerFile string) ([]Outpu fanOut(partners, deliveryChannels...) for o := range merge(out...) { - if v, ok := outMap[o.DeliveryID]; !ok || (o.IsPossible && !v.IsPossible) || (o.IsPossible && o.Cost < v.Cost) { - outMap[o.DeliveryID] = o - } - } - - for _, o := range outMap { result = append(result, o) } From d870650dc5056c6d000cc83583470f120f550faa Mon Sep 17 00:00:00 2001 From: Oleksandr Petrykin Date: Fri, 4 Nov 2022 21:27:58 +0200 Subject: [PATCH 3/4] Problem 2 --- cmd/problem1/main.go | 2 +- cmd/problem2/main.go | 8 +- capacities.csv => data/capacities.csv | 2 +- delivery/{entities.go => entity.go} | 0 delivery/repository.go | 12 +- delivery/service.go | 196 +++++++++++++++++++------- file/util.go | 61 ++++++-- util/util.go | 39 +++++ 8 files changed, 252 insertions(+), 68 deletions(-) rename capacities.csv => data/capacities.csv (61%) rename delivery/{entities.go => entity.go} (100%) create mode 100644 util/util.go diff --git a/cmd/problem1/main.go b/cmd/problem1/main.go index eab6c13..1563bd6 100644 --- a/cmd/problem1/main.go +++ b/cmd/problem1/main.go @@ -4,7 +4,7 @@ import "github.com/challenge2019/delivery" func main() { d := delivery.NewDeliveryService(&delivery.Repository{}) - _, err := d.FindMinCostPartners("./data/input.csv", "./data/partners.csv") + _, err := d.FindMinCostPartners("./data/input.csv") if err != nil { panic(err) } diff --git a/cmd/problem2/main.go b/cmd/problem2/main.go index 7905807..5f16642 100644 --- a/cmd/problem2/main.go +++ b/cmd/problem2/main.go @@ -1,5 +1,11 @@ package main -func main() { +import "github.com/challenge2019/delivery" +func main() { + d := delivery.NewDeliveryService(&delivery.Repository{}) + _, err := d.Assign("./data/input.csv") + if err != nil { + panic(err) + } } diff --git a/capacities.csv b/data/capacities.csv similarity index 61% rename from capacities.csv rename to data/capacities.csv index ae622cf..fe6943d 100644 --- a/capacities.csv +++ b/data/capacities.csv @@ -1,4 +1,4 @@ -"Partner ID","Capacity (in GB)" +partner_id, capacity P1 ,350 P2 ,500 P3 ,1500 diff --git a/delivery/entities.go b/delivery/entity.go similarity index 100% rename from delivery/entities.go rename to delivery/entity.go diff --git a/delivery/repository.go b/delivery/repository.go index 37340e0..1059cca 100644 --- a/delivery/repository.go +++ b/delivery/repository.go @@ -4,12 +4,16 @@ import "github.com/challenge2019/file" type Repository struct{} -func (r *Repository) FetchDeliveries(c chan<- error, fileName string, deliveries *[]Input) { - file.ReadAsync(c, fileName, deliveries) +func (r *Repository) FetchDeliveries(fileName string, deliveries *[]Input) chan error { + return file.ReadAsync(fileName, deliveries) } -func (r *Repository) FetchPartners(c chan<- error, fileName string, partners *[]Partner) { - file.ReadAsync(c, fileName, partners) +func (r *Repository) FetchPartners(fileName string, partners *[]Partner) chan error { + return file.ReadAsync(fileName, partners) +} + +func (r *Repository) FetchCapacities(fileName string, capacities *map[TrimString]int) chan error { + return file.ReadToMapAsync(fileName, capacities) } func (r *Repository) SaveDeliveriesOutput(fileName string, out *[]Output) error { diff --git a/delivery/service.go b/delivery/service.go index df0c3b6..2231341 100644 --- a/delivery/service.go +++ b/delivery/service.go @@ -1,7 +1,12 @@ package delivery import ( - "sync" + "github.com/challenge2019/util" +) + +const ( + partnerFileName = "./data/partners.csv" + capacityFileName = "./data/capacities.csv" ) type Service struct { @@ -12,42 +17,6 @@ func NewDeliveryService(r *Repository) *Service { return &Service{Repo: r} } -func fanOut(partners []Partner, out ...chan Partner) { - go func() { - for _, p := range partners { - for _, c := range out { - c <- p - } - } - - for _, c := range out { - close(c) - } - }() -} - -func merge(in ...chan Output) chan Output { - wg := sync.WaitGroup{} - out := make(chan Output, len(in)) - wg.Add(len(in)) - - for _, c := range in { - go func(ch chan Output) { - defer wg.Done() - for t := range ch { - out <- t - } - }(c) - } - - go func() { - wg.Wait() - close(out) - }() - - return out -} - func (s *Service) checkPartner(in <-chan Partner, d Input) chan Output { out := make(chan Output) min := Output{DeliveryID: d.DeliveryID, IsPossible: false} @@ -68,20 +37,19 @@ func (s *Service) checkPartner(in <-chan Partner, d Input) chan Output { return out } -func (s *Service) FindMinCostPartners(deliveryFile, partnerFile string) ([]Output, error) { +func (s *Service) FindMinCostPartners(input string) ([]Output, error) { var ( deliveries []Input partners []Partner ) - filesCount := 2 - errors := make(chan error, filesCount) - - go s.Repo.FetchDeliveries(errors, deliveryFile, &deliveries) - go s.Repo.FetchPartners(errors, partnerFile, &partners) + errors := util.Merge( + s.Repo.FetchDeliveries(input, &deliveries), + s.Repo.FetchPartners(partnerFileName, &partners), + ) - for i := 0; i < filesCount; i++ { - if err := <-errors; err != nil { + for err := range errors { + if err != nil { return nil, err } } @@ -97,16 +65,148 @@ func (s *Service) FindMinCostPartners(deliveryFile, partnerFile string) ([]Outpu out = append(out, s.checkPartner(c, deliveries[i])) } - fanOut(partners, deliveryChannels...) + util.FanOut(partners, deliveryChannels...) - for o := range merge(out...) { + for o := range util.Merge(out...) { result = append(result, o) } - err := s.Repo.SaveDeliveriesOutput("./data/output.csv", &result) + err := s.Repo.SaveDeliveriesOutput("./data/output_problem1.csv", &result) if err != nil { return nil, err } return result, nil } + +func toMap(pp []Partner) map[TrimString][]Partner { + m := make(map[TrimString][]Partner) + + for _, p := range pp { + m[p.TheatreID] = append(m[p.TheatreID], p) + } + + return m +} + +type container struct { + value int + okCount int + deliveries []Output + capacityLeft map[TrimString]int +} + +func (c *container) copyAndAdd(d Input, p Partner) container { + cl := copyMap(c.capacityLeft) + out := Output{DeliveryID: d.DeliveryID, IsPossible: false} + ok := c.okCount + value := c.value + if cl[p.PartnerID] >= d.Amount { + ok++ + cost := d.Amount * p.CostPerGB + if cost < p.MinCost { + cost = p.MinCost + } + value += cost + out = Output{DeliveryID: d.DeliveryID, IsPossible: true, Cost: cost, PartnerID: p.PartnerID} + } + cl[p.PartnerID] = cl[p.PartnerID] - d.Amount + + dd := make([]Output, len(c.deliveries), len(c.deliveries)+1) + copy(dd, c.deliveries) + + return container{ + value: value, + capacityLeft: cl, + okCount: ok, + deliveries: append(dd, out), + } +} + +func copyMap(m map[TrimString]int) map[TrimString]int { + cm := make(map[TrimString]int) + for k, v := range m { + cm[k] = v + } + return cm +} + +func newContainer(d Input, p Partner, cc map[TrimString]int) container { + var value int + cl := copyMap(cc) + out := Output{DeliveryID: d.DeliveryID, IsPossible: false} + ok := 0 + + if cl[p.PartnerID] >= d.Amount { + ok++ + value = d.Amount * p.CostPerGB + if value < p.MinCost { + value = p.MinCost + } + out = Output{DeliveryID: d.DeliveryID, IsPossible: true, Cost: value, PartnerID: p.PartnerID} + } + cl[p.PartnerID] = cl[p.PartnerID] - d.Amount + + return container{ + value: value, + capacityLeft: cl, + okCount: ok, + deliveries: []Output{out}, + } +} + +func (s *Service) Assign(input string) ([]Output, error) { + var ( + deliveries []Input + partners []Partner + capacities map[TrimString]int + ) + + errors := util.Merge( + s.Repo.FetchDeliveries(input, &deliveries), + s.Repo.FetchPartners(partnerFileName, &partners), + s.Repo.FetchCapacities(capacityFileName, &capacities), + ) + + for err := range errors { + if err != nil { + return nil, err + } + } + + partnersMap := toMap(partners) + + var ( + containerSet []container + opt container + ) + + for _, d := range deliveries { + pp := partnersMap[d.TheatreID] + var cc []container + for _, p := range pp { + if d.Amount < p.Slab.MinSlab || d.Amount > p.Slab.MaxSlab { + continue + } + + cc = append(cc, newContainer(d, p, capacities)) + + for i := 0; i < len(containerSet); i++ { + newC := containerSet[i].copyAndAdd(d, p) + cc = append(cc, newC) + + if newC.okCount > opt.okCount || (newC.okCount == opt.okCount && newC.value <= opt.value) { + opt = newC + } + } + } + containerSet = append(containerSet, cc...) + } + + err := s.Repo.SaveDeliveriesOutput("./data/output_problem2.csv", &opt.deliveries) + if err != nil { + return nil, err + } + + return opt.deliveries, nil +} diff --git a/file/util.go b/file/util.go index c573230..3415e0e 100644 --- a/file/util.go +++ b/file/util.go @@ -1,26 +1,61 @@ package file import ( + "encoding/csv" "github.com/gocarina/gocsv" "os" ) -func ReadAsync(c chan<- error, fileName string, out interface{}) { - file, err := os.Open(fileName) - if err != nil { - c <- err - return - } +func ReadAsync(fileName string, out interface{}) chan error { + c := make(chan error) - defer file.Close() + go func() { + defer close(c) - if err := gocsv.UnmarshalFile(file, out); err != nil { - c <- err - return - } + file, err := os.Open(fileName) + if err != nil { + c <- err + return + } - c <- nil - return + defer file.Close() + + if err := gocsv.UnmarshalFile(file, out); err != nil { + c <- err + return + } + + c <- nil + }() + + return c +} + +func ReadToMapAsync(fileName string, out interface{}) chan error { + c := make(chan error) + + go func() { + defer close(c) + + file, err := os.Open(fileName) + if err != nil { + c <- err + return + } + + r := csv.NewReader(file) + + defer file.Close() + + if err := gocsv.UnmarshalCSVToMap(r, out); err != nil { + c <- err + return + } + + c <- nil + }() + + return c } func Write(fileName string, data interface{}) (err error) { diff --git a/util/util.go b/util/util.go new file mode 100644 index 0000000..64f0767 --- /dev/null +++ b/util/util.go @@ -0,0 +1,39 @@ +package util + +import "sync" + +func FanOut[T any](partners []T, out ...chan T) { + go func() { + for _, p := range partners { + for _, c := range out { + c <- p + } + } + + for _, c := range out { + close(c) + } + }() +} + +func Merge[T any](in ...chan T) chan T { + wg := sync.WaitGroup{} + out := make(chan T, len(in)) + wg.Add(len(in)) + + for _, c := range in { + go func(ch chan T) { + defer wg.Done() + for t := range ch { + out <- t + } + }(c) + } + + go func() { + wg.Wait() + close(out) + }() + + return out +} From 8decb5456e0f618a4cf0444f5aa2a3135fe43639 Mon Sep 17 00:00:00 2001 From: Oleksandr Petrykin Date: Fri, 4 Nov 2022 21:46:28 +0200 Subject: [PATCH 4/4] Problem 2 --- data/output_problem1.csv | 5 +++++ data/output_problem2.csv | 5 +++++ delivery/service.go | 19 ++++++++++--------- 3 files changed, 20 insertions(+), 9 deletions(-) create mode 100644 data/output_problem1.csv create mode 100644 data/output_problem2.csv diff --git a/data/output_problem1.csv b/data/output_problem1.csv new file mode 100644 index 0000000..8cd7808 --- /dev/null +++ b/data/output_problem1.csv @@ -0,0 +1,5 @@ +delivery_id,is_possible,partner_id,cost +D2,true,P3,3900 +D1,true,P3,3750 +D3,true,P3,15300 +D4,false,,0 diff --git a/data/output_problem2.csv b/data/output_problem2.csv new file mode 100644 index 0000000..361b1d0 --- /dev/null +++ b/data/output_problem2.csv @@ -0,0 +1,5 @@ +delivery_id,is_possible,partner_id,cost +D1,true,P1,2000 +D2,true,P2,3500 +D3,true,P3,15300 +D4,false,,0 diff --git a/delivery/service.go b/delivery/service.go index 2231341..aed8cbd 100644 --- a/delivery/service.go +++ b/delivery/service.go @@ -96,21 +96,26 @@ type container struct { capacityLeft map[TrimString]int } +func (c *container) checkOptimal(opt container) bool { + return c.okCount > opt.okCount || (c.okCount == opt.okCount && c.value <= opt.value && len(c.deliveries) >= len(opt.deliveries)) +} + func (c *container) copyAndAdd(d Input, p Partner) container { cl := copyMap(c.capacityLeft) out := Output{DeliveryID: d.DeliveryID, IsPossible: false} ok := c.okCount value := c.value - if cl[p.PartnerID] >= d.Amount { + + if cl[p.PartnerID] >= d.Amount && d.Amount >= p.Slab.MinSlab && d.Amount <= p.Slab.MaxSlab { ok++ cost := d.Amount * p.CostPerGB if cost < p.MinCost { cost = p.MinCost } value += cost + cl[p.PartnerID] = cl[p.PartnerID] - d.Amount out = Output{DeliveryID: d.DeliveryID, IsPossible: true, Cost: cost, PartnerID: p.PartnerID} } - cl[p.PartnerID] = cl[p.PartnerID] - d.Amount dd := make([]Output, len(c.deliveries), len(c.deliveries)+1) copy(dd, c.deliveries) @@ -137,15 +142,15 @@ func newContainer(d Input, p Partner, cc map[TrimString]int) container { out := Output{DeliveryID: d.DeliveryID, IsPossible: false} ok := 0 - if cl[p.PartnerID] >= d.Amount { + if cl[p.PartnerID] >= d.Amount && d.Amount >= p.Slab.MinSlab && d.Amount <= p.Slab.MaxSlab { ok++ value = d.Amount * p.CostPerGB if value < p.MinCost { value = p.MinCost } + cl[p.PartnerID] = cl[p.PartnerID] - d.Amount out = Output{DeliveryID: d.DeliveryID, IsPossible: true, Cost: value, PartnerID: p.PartnerID} } - cl[p.PartnerID] = cl[p.PartnerID] - d.Amount return container{ value: value, @@ -185,17 +190,13 @@ func (s *Service) Assign(input string) ([]Output, error) { pp := partnersMap[d.TheatreID] var cc []container for _, p := range pp { - if d.Amount < p.Slab.MinSlab || d.Amount > p.Slab.MaxSlab { - continue - } - cc = append(cc, newContainer(d, p, capacities)) for i := 0; i < len(containerSet); i++ { newC := containerSet[i].copyAndAdd(d, p) cc = append(cc, newC) - if newC.okCount > opt.okCount || (newC.okCount == opt.okCount && newC.value <= opt.value) { + if newC.checkOptimal(opt) { opt = newC } }