diff --git a/src/combine-layouts.rs b/src/combine-layouts.rs
index 779705b..8ef885c 100644
--- a/src/combine-layouts.rs
+++ b/src/combine-layouts.rs
@@ -1,5 +1,5 @@
-use db::LayoutDB;
-use city::{City, SIZE};
+use db::{LayoutDB, SavedLayout};
+use city::{City, House, SIZE};
 use itertools::Itertools;
 use crate::combine::transpose_layout;
 
@@ -23,36 +23,40 @@ fn main() {
     let transposed_city = transpose_city(&city);
     eprintln!("Finished building a transposed city");
 
-    const TOP_LAYOUT_COUNT: usize = 100;
+    const TOP_LAYOUT_COUNT: usize = 300;
+
+    let mut cache = combine::CompatibilityCache::new();
 
     let mut last_improve_step = LastStep::None;
     loop {
         if last_improve_step == LastStep::Vertical { break; }
         eprintln!("Starting to combine {} top houses DB; vertical cuts", TOP_LAYOUT_COUNT);
-        let sorted: Vec<_> = db.layouts().iter()
+        let sorted: Vec<SavedLayout> = db.layouts().iter()
             .sorted_by(|x, y| city::get_price(&city, x.houses()).cmp(&city::get_price(&city, y.houses())))
-            .map(|layout| layout.clone())
+            .map(|layout| (*layout).clone())
             .collect();
-        let chosen_layouts: Vec<_> = sorted.iter().take(TOP_LAYOUT_COUNT).map(|l| l.houses().clone()).collect();
-        if combine::try_combine(&city, &chosen_layouts, &chosen_layouts, &mut db, false) {
+        let chosen_layouts: Vec<_> = sorted.into_iter().take(TOP_LAYOUT_COUNT).collect();
+        if combine::try_combine(&city, &chosen_layouts, &chosen_layouts, &mut db, &mut cache, false) {
             last_improve_step = LastStep::Vertical;
         }
         eprintln!("Finished vertical cuts, improvement: {}", last_improve_step == LastStep::Vertical);
 
         if last_improve_step == LastStep::Horizontal { break; }
 
-        let sorted: Vec<_> = db.layouts().iter()
+        let sorted: Vec<SavedLayout> = db.layouts().iter()
             .sorted_by(|x, y| city::get_price(&city, x.houses()).cmp(&city::get_price(&city, y.houses())))
-            .map(|layout| layout.clone())
+            .map(|layout| (*layout).clone())
             .collect();
-        let chosen_layouts: Vec<_> = sorted.iter().take(TOP_LAYOUT_COUNT).map(|l| l.houses().clone()).collect();
-        let transposed_chosen_layouts: Vec<_> = chosen_layouts.iter().map(|x| transpose_layout(x)).collect();
+        let chosen_layouts: Vec<_> = sorted.into_iter().take(TOP_LAYOUT_COUNT).collect();
+        let transposed_chosen_layouts: Vec<_> = chosen_layouts.iter().map(|x| transpose_saved_layout(x)).collect();
 
         eprintln!("Starting to combine {} top houses DB; horizontal cuts", TOP_LAYOUT_COUNT);
-        if combine::try_combine(&transposed_city, &transposed_chosen_layouts, &transposed_chosen_layouts, &mut db, true) {
+        if combine::try_combine(&transposed_city, &transposed_chosen_layouts, &transposed_chosen_layouts, &mut db, &mut cache, true) {
             last_improve_step = LastStep::Horizontal;
         }
         eprintln!("Finished horizontal cuts, improvement: {}", last_improve_step == LastStep::Horizontal);
+
+        if last_improve_step == LastStep::None { break; }
     }
 }
 
@@ -68,3 +72,12 @@ fn transpose_city(city: &City) -> City {
 
     City::new(transposed_prices)
 }
+
+fn transpose_saved_layout(layout: &SavedLayout) -> SavedLayout {
+    let mut transposed = Vec::new();
+    for house in layout.houses() {
+        transposed.push(House::new(house.y, house.x));
+    }
+
+    SavedLayout::new(layout.id(), transposed)
+}
diff --git a/src/combine.rs b/src/combine.rs
index b68234c..643e6e6 100644
--- a/src/combine.rs
+++ b/src/combine.rs
@@ -1,40 +1,75 @@
 use crate::city;
-use crate::db::LayoutDB;
+use crate::db::{LayoutDB, SavedLayout};
 use crate::city::{City, House, SIZE};
 use itertools::Itertools;
 use itertools::iproduct;
-use std::collections::VecDeque;
+use std::collections::{VecDeque, HashMap};
 use std::collections::vec_deque::Iter;
 
-pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: &Vec<Vec<House>>, db: &mut LayoutDB, transposed: bool) -> bool {
-    let mut improved = false;
+struct LeftState<'a> {
+    layout: &'a SavedLayout,
+    sorted_houses: Vec<House>,
+    line: LeftLine
+}
 
-    // Sorted in reverse so we can remove from the end
-    let mut left_houses_sorted: Vec<Vec<House>> = left_layouts.iter()
-        .map(|l| l.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect())
-        .collect();
-    let right_houses_sorted: Vec<Vec<House>> = right_layouts.iter()
-        .map(|l| l.iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect())
-        .collect();
+struct RightState<'a> {
+    layout: &'a SavedLayout,
+    line: RightLine
+}
+
+pub struct CompatibilityCache {
+    map: HashMap<(usize, usize, usize, bool), bool>
+}
+
+impl CompatibilityCache {
+    pub fn new() -> CompatibilityCache {
+        CompatibilityCache {
+            map: HashMap::new()
+        }
+    }
+
+    pub fn is_compatible(&self, left_layout: &SavedLayout, right_layout: &SavedLayout, index: usize, y_axis: bool) -> Option<&bool> {
+        self.map.get(&(left_layout.id(), right_layout.id(), index, y_axis))
+    }
+
+    pub fn set_compatible(&mut self, left_layout: &SavedLayout, right_layout: &SavedLayout, index: usize, y_axis: bool, compatible: bool) {
+        self.map.insert((left_layout.id(), right_layout.id(), index, y_axis), compatible);
+    }
+}
+
+pub fn try_combine(city: &City, left_layouts: &Vec<SavedLayout>, right_layouts: &Vec<SavedLayout>, db: &mut LayoutDB, cache: &mut CompatibilityCache, transposed: bool) -> bool {
+    let mut improved = false;
 
     let mut lefts = Vec::new();
     let mut rights = Vec::new();
 
-    for _ in left_layouts {
-        lefts.push(LeftLine::new());
+    for left_layout in left_layouts {
+        // Sorted in reverse so we can remove from the end
+        let sorted_houses: Vec<_> = left_layout.houses().iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect();
+
+        lefts.push(LeftState {
+            layout: &left_layout,
+            sorted_houses,
+            line: LeftLine::new()
+        });
     }
 
-    for mut right_layout in right_houses_sorted {
-        let mut right = RightLine::new();
+    for right_layout in right_layouts {
+        let mut sorted_houses: Vec<_> = right_layout.houses().iter().sorted_by(|h1, h2| h2.x.cmp(&h1.x)).map(|x| *x).collect();
+
+        let mut line = RightLine::new();
         // Make sure that we include all houses initially
-        while let Some(house) = right_layout.pop() {
-            right.add_house(house, &city);
+        while let Some(house) = sorted_houses.pop() {
+            line.add_house(house, &city);
         }
-        rights.push(right);
+        rights.push(RightState {
+            layout: right_layout,
+            line
+        });
     }
 
     let mut best_price = left_layouts.iter().chain(right_layouts.iter())
-        .map(|layout| city::get_price(&city, layout))
+        .map(|layout| city::get_price(&city, layout.houses()))
         .min();
 
     let axis = if transposed { "y" } else { "x" };
@@ -44,11 +79,11 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: &
         eprintln!("Starting {} {}", axis, x);
 
         // Update the lines
-        for (left_line, left_houses) in lefts.iter_mut().zip(left_houses_sorted.iter_mut()) {
-            while let Some(house) = left_houses.last() {
+        for left in lefts.iter_mut() {
+            while let Some(house) = left.sorted_houses.last() {
                 if house.x == x {
-                    left_line.add_house(*house, &city);
-                    left_houses.pop();
+                    left.line.add_house(*house, &city);
+                    left.sorted_houses.pop();
                 } else {
                     break;
                 }
@@ -56,7 +91,7 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: &
         }
 
         for right_line in rights.iter_mut() {
-            right_line.remove_houses(x, &city);
+            right_line.line.remove_houses(x, &city);
         }
 
         // Check compatibility of lines
@@ -66,21 +101,37 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: &
             continue;
         }
 
-        let pairs: Vec<_> = iproduct!(lefts.iter().enumerate(), rights.iter().enumerate())
+        let pairs = iproduct!(lefts.iter().enumerate(), rights.iter().enumerate())
             .filter(|((left_i, _), (right_i, _))| left_i != right_i)
-            .map(|((left_i, left), (right_i, right))| (left, right, left.price + right.price, left_i, right_i))
-            .sorted_by(|(_, _, price1, _, _), (_, _, price2, _, _)| price1.cmp(&price2))
-            .collect();
+            .map(|((left_i, left), (right_i, right))| (left, right, left.line.price + right.line.price, left_i, right_i))
+            .sorted_by(|(_, _, price1, _, _), (_, _, price2, _, _)| price1.cmp(&price2));
 
         let mut compatibles = 0;
         let mut incompatibles = 0;
+        let mut cache_hits = 0;
         for (left, right, price, left_i, right_i) in pairs {
-            if is_compatible(city, &left, &right) {
+            if let Some(min_price) = best_price {
+                if price >= min_price {
+                    break;
+                }
+            }
+            let compatible = match cache.is_compatible(left.layout, right.layout, x, transposed) {
+                None => {
+                    let compatible = is_compatible(city, &left.line, &right.line);
+                    cache.set_compatible(left.layout, right.layout, x, transposed, compatible);
+                    compatible
+                }
+                Some(compatible) => {
+                    cache_hits += 1;
+                    *compatible
+                }
+            };
+            if compatible {
                 if best_price.is_none() || price < best_price.unwrap() {
                     best_price = Some(price);
                     eprintln!("{} - new best score, cut on {} {}, left {} - right {}, printing", price, axis, x, left_i, right_i);
                     println!("{} - new best score, cut on {} {}, left {} - right {}", price, axis, x, left_i, right_i);
-                    let mut new_houses: Vec<_> = left.houses().copied().chain(right.houses().copied()).collect();
+                    let mut new_houses: Vec<_> = left.line.houses().copied().chain(right.line.houses().copied()).collect();
                     if transposed {
                         new_houses = transpose_layout(&new_houses);
                     }
@@ -90,7 +141,7 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: &
                     }
 
                     // We only add best results to avoid overfilling the database with similar layouts
-                    db.add_layout(&new_houses, true);
+                    db.add_layout(&new_houses, false);
                     improved = true;
                 }
                 compatibles += 1;
@@ -102,7 +153,7 @@ pub fn try_combine(city: &City, left_layouts: &Vec<Vec<House>>, right_layouts: &
             }
         }
 
-        eprintln!("{} incompatibles checked before {} compatible", incompatibles, compatibles);
+        eprintln!("{} incompatibles checked before {} compatible ({} cache hits)", incompatibles, compatibles, cache_hits);
     }
 
     improved
diff --git a/src/db.rs b/src/db.rs
index 5dd649b..5b47138 100644
--- a/src/db.rs
+++ b/src/db.rs
@@ -7,12 +7,17 @@ pub struct LayoutDB {
     layouts: Vec<SavedLayout>
 }
 
+#[derive(Clone)]
 pub struct SavedLayout {
     id: usize,
     houses: Vec<House>
 }
 
 impl SavedLayout {
+    pub fn new(id: usize, houses: Vec<House>) -> Self {
+        SavedLayout { id, houses }
+    }
+
     pub fn houses(&self) -> &Vec<House> {
         &self.houses
     }