十一月的陽光透過窗戶,照射在一位笑起來甜美、青春洋溢的女子的辦公桌上。小悅,一個總是以高馬尾造型亮相的軟體工程師,展現出她的幹練與活力。那烏黑亮麗的長髮輕盈飄動,彷彿在訴說著她的獨特魅力。她的眉眼如畫,那雙明亮的眼睛裡閃爍著對知識的渴望和對技術挑戰的熱情。
這一天,她收到了一封來自醫院的郵件,郵件中提到的掃描裝置技術更新問題讓她感到有些挑戰。然而,對於技術挑戰,小悅總是充滿了好奇心和熱情。她決定主動聯絡醫院,表達自己願意參與這個專案的意願。幸運的是,醫院方面很快回復了她的郵件,並安排了一次電話會議。
在電話會議中,小悅與醫院的管理人員和相關領域的專家進行了交流。他們的聲音充滿了對新技術和新思維的渴望。小悅也意識到這次機會對於自己的事業發展可能是一個重大的突破。
不久之後,小悅成功加入了醫院方面的團隊。在團隊中,她發現了很多優秀的專家和工程師。其中有一位資深醫生對掃描裝置的需求非常瞭解,而另一位工程師則對影像處理演算法有深入的研究。小悅深知自己在這個團隊中擔任著關鍵的角色,必須充分發揮自己的優勢。
在團隊的合作中,小悅與大家建立了良好的合作關係。她不斷地與團隊成員溝通和交流,瞭解他們的需求和想法。她的聲音總是溫柔而自信,讓人感到安心和信任。同時,她也向團隊成員分享了自己的經驗和專業知識,為專案的進展做出了巨大的貢獻。
然而,隨著專案的深入,小悅逐漸發現了一些技術難題。其中最大的問題是如何計算陰影部分的面積並集。為了解決這個問題,她不斷地查閱文獻、研究演算法,並嘗試了多種方法。有時候,她會陷入深深的困境,甚至整晚都無法入睡。但是,她從未放棄過對這個問題的探索和解決。
經過一段時間的努力,小悅終於提出了一種有效的方法來計算陰影部分的面積並集。這個方法不僅得到了團隊的認可和支援,也成功地解決了專案中的一大難題。小悅的貢獻讓整個團隊都感到非常驚喜和敬佩。
在接下來的時間中,小悅和團隊繼續努力工作,終於成功地開發出了一款新的掃描裝置程式設計。這款程式採用了小悅提出的陰影面積計算方法,有效地提高了掃描的準確性和效率。醫院方面對這款程式非常滿意,並決定將其投入使用。
在這個過程中,小悅學到了很多東西。她不僅在技術方面取得了突破和成長,還學會了如何與不同背景的人合作和溝通。她深刻地認識到人際關係的重要性以及如何利用人際關係來擴充自己的視野和能力。同時,她也體驗到了幫助他人的喜悅和成就感。
小悅面臨的問題是,醫學影像重建:醫學影像(如CT、MRI等)通常是由多個切片影像組成的,這些影像可能存在重疊或交叉的區域。矩形面積並集演算法可以用於計算這些影像之間的重疊區域,從而實現準確的影像重建和融合。
她需要開發一個名為Calculate的方法,該方法接受一個二維陣列作為引數,其中每個子陣列表示一個矩形的座標。在這個例子中,三個矩形的座標分別為[1,2,5,6]、[1,3,4,5]和[3,1,5,4]。並返回期望的面積並集。
每個矩形表示為:[x0,y0,x1,y1]
(x0,y0)-矩形左下角的座標
(x1,y1)-矩形右上角的座標
圖例(面積=18):
演算法實現1:
1 private class Rectangle // 定義一個名為Rectangle的私有類 2 { 3 public long x0; // 矩形的左邊界 4 public long y0; // 矩形的底邊界 5 public long x1; // 矩形的右邊界 6 public long y1; // 矩形的頂邊界 7 public bool ToDelete = false; // 標記是否需要刪除該矩形 8 9 public Rectangle(long x0, long y0, long x1, long y1) // 建構函式,用於初始化矩形物件的邊界值 10 { 11 this.x0 = x0; 12 this.x1 = x1; 13 this.y0 = y0; 14 this.y1 = y1; 15 } 16 17 public long S() // 計算矩形的面積 18 { 19 return (x1 - x0) * (y1 - y0); 20 } 21 22 public bool IsInside(Rectangle r) // 判斷當前矩形是否完全包含另一個矩形r 23 { 24 return ((r.x0 >= x0) && (r.x1 <= x1) && (r.y0 >= y0) && (r.y1 <= y1)) ? true : false; 25 } 26 27 public bool IsOutside(Rectangle r) // 判斷當前矩形是否完全在另一個矩形r的外部 28 { 29 return ((r.x0 >= x1) || (r.x1 <= x0) || (r.y0 >= y1) || (r.y1 <= y0)) ? true : false; 30 } 31 32 public List<Rectangle> GetIntersection(Rectangle r) // 獲取當前矩形與另一個矩形r的相交部分 33 { 34 List<Rectangle> rests = new List<Rectangle>(); 35 if (r.x0 < x0) 36 rests.Add(new Rectangle(r.x0, r.y0, x0, r.y1)); 37 if (r.x1 > x1) 38 rests.Add(new Rectangle(x1, r.y0, r.x1, r.y1)); 39 if (r.y0 < y0) 40 rests.Add(new Rectangle(Math.Max(r.x0, x0), r.y0, Math.Min(r.x1, x1), y0)); 41 if (r.y1 > y1) 42 rests.Add(new Rectangle(Math.Max(r.x0, x0), y1, Math.Min(r.x1, x1), r.y1)); 43 return rests; 44 } 45 } 46 47 public static long Calculate(IEnumerable<int[]> rectangles) // 定義一個名為Calculate的靜態方法,用於計算矩形的最大面積 48 { 49 long maxS = 0; // 最大面積的初始值為0 50 List<Rectangle> lastRectangle = new List<Rectangle>(rectangles.Select(x => new Rectangle((long)x[0], (long)x[1], (long)x[2], (long)x[3]))); // 將傳入的矩形引數轉換為Rectangle物件,並新增到lastRectangle列表中 51 while (lastRectangle.Count > 0) // 當lastRectangle列表不為空時,進行迴圈 52 { 53 Rectangle top = lastRectangle.First(); // 獲取lastRectangle列表的第一個矩形物件 54 lastRectangle.Remove(top); // 從lastRectangle列表中移除該矩形物件 55 List<Rectangle> intersect = new List<Rectangle>() { top }; // 建立一個名為intersect的列表,初始值為包含top矩形物件的列表 56 var intersected = lastRectangle.Where(x => !x.IsOutside(top)); // 從lastRectangle列表中篩選出與top矩形相交的矩形物件,並儲存在intersected變數中 57 foreach (Rectangle r in intersected) // 遍歷intersected列表中的每個矩形物件 58 { 59 intersect.RemoveAll(x => r.IsInside(x)); // 從intersect列表中移除完全被r矩形包含的矩形物件 60 List<Rectangle> newIntersections = new List<Rectangle>(); 61 foreach (Rectangle x in intersect) // 遍歷intersect列表中的每個矩形物件 62 if (!r.IsOutside(x)) // 如果r矩形與x矩形相交 63 { 64 newIntersections.AddRange(r.GetIntersection(x)); // 獲取r矩形與x矩形的相交部分,並新增到newIntersections列表中 65 x.ToDelete = true; // 標記x矩形需要刪除 66 } 67 intersect.RemoveAll(x => x.ToDelete); // 從intersect列表中移除需要刪除的矩形物件 68 intersect.AddRange(newIntersections); // 將newIntersections列表中的矩形物件新增到intersect列表中 69 if (intersect.Count == 0) // 如果intersect列表為空,跳出迴圈 70 break; 71 } 72 if (intersect.Count > 0) // 如果intersect列表不為空 73 maxS += intersect.Sum(x => x.S()); // 將intersect列表中每個矩形物件的面積相加,並累加到maxS變數中 74 } 75 return maxS; // 返回最大面積maxS 76 }
首先,矩形的相交關係是指兩個矩形是否有共同的區域。我們可以透過判斷兩個矩形的邊界是否有重疊來確定它們是否相交。如果兩個矩形的邊界有重疊,則它們相交;否則,它們不相交。
其次,矩形的包含關係是指一個矩形是否完全包含另一個矩形。我們可以透過比較兩個矩形的邊界來確定包含關係。如果一個矩形的邊界完全包含在另一個矩形的邊界內部,則前者包含後者;否則,前者不包含後者。
在演算法中,我們首先將傳入的矩形引數轉換為Rectangle
物件,並儲存在lastRectangle
列表中。然後,透過迴圈處理lastRectangle
列表中的矩形物件,找到與當前矩形相交的其他矩形物件,並計算它們的相交部分。
在處理每個矩形物件時,我們首先找到與當前矩形相交的矩形物件,並將它們儲存在intersected
變數中。然後,我們遍歷intersected
列表中的每個矩形物件,將完全被當前矩形包含的矩形物件從intersect
列表中移除。接下來,對於與當前矩形相交的每個矩形物件,我們計算它們的相交部分,並將相交部分新增到intersect
列表中。最後,我們將intersect
列表中的矩形物件的面積相加,並累加到maxS
變數中。
透過這種逐步計算矩形的相交部分,我們可以找到一組矩形的最大面積。這是因為我們透過不斷更新intersect
列表來剔除已經被其他矩形完全包含的矩形,只保留與其他矩形相交的部分。最終,我們將intersect
列表中的矩形物件的面積相加,得到這組矩形的最大面積。
這個演算法的數學原理基於矩形的幾何性質和集合運算的概念,透過對矩形的相交和包含關係進行處理,最終得到最大面積並集。
演算法實現2:
1 struct Rect // 定義一個名為Rect的結構體 2 { 3 public int l, r, t, b; // 定義四個整型變數,分別表示矩形的左、右、上、下邊界 4 public long area => (long)(r - l) * (t - b); // 定義一個名為area的只讀屬性,表示矩形的面積 5 public bool exist => l < r && b < t; // 定義一個名為exist的只讀屬性,表示矩形是否存在 6 7 public static Rect Create(params int[] c) => // 定義一個名為Create的靜態方法,用於建立一個新的Rect物件 8 new Rect() { l = c[0], b = c[1], r = c[2], t = c[3] }; 9 10 public bool intersects(Rect rc, out Rect result) => // 定義一個名為intersects的方法,用於判斷兩個矩形是否相交,並返回相交部分的矩形物件 11 (result = Create( 12 Math.Max(l, rc.l), Math.Max(b, rc.b), 13 Math.Min(r, rc.r), Math.Min(t, rc.t))) 14 .exist; 15 16 public Rect[] octants(int n = int.MinValue, int p = int.MaxValue) => // 定義一個名為octants的方法,用於將當前矩形分成八個象限,並返回八個象限的矩形物件陣列 17 new [] { 18 Create(n, n, l, b), 19 Create(n, b, l, t), 20 Create(n, t, l, p), 21 Create(l, t, r, p), 22 Create(r, t, p, p), 23 Create(r, b, p, t), 24 Create(r, n, p, b), 25 Create(l, n, r, b) 26 }; 27 } 28 29 class OctTreeNode // 定義一個名為OctTreeNode的類 30 { 31 Rect origin; // 定義一個名為origin的Rect物件,表示當前節點所代表的矩形 32 Rect[] oct_bounds; // 定義一個名為oct_bounds的Rect物件陣列,表示當前節點所代表的矩形被分成的八個象限 33 OctTreeNode[] oct_values; // 定義一個名為oct_values的OctTreeNode物件陣列,表示當前節點所代表的矩形被分成的八個象限所代表的子節點 34 35 public OctTreeNode(Rect rc) // 定義一個建構函式,用於建立一個新的OctTreeNode物件 36 { 37 origin = rc; // 將傳入的Rect物件賦值給origin變數 38 oct_bounds = rc.octants(); // 將當前矩形分成八個象限,並賦值給oct_bounds變數 39 oct_values = new OctTreeNode[8]; // 建立一個長度為8的OctTreeNode物件陣列,並賦值給oct_values變數 40 } 41 42 public void insert(Rect rc) // 定義一個名為insert的方法,用於向當前節點插入一個新的矩形物件 43 { 44 for (int i = 0; i < 8; i++) // 遍歷oct_bounds陣列中的每個矩形物件 45 { 46 if (oct_bounds[i].intersects(rc, out var part)) // 如果當前矩形與遍歷到的矩形相交 47 { 48 oct_values[i] ?. insert(part); // 如果oct_values陣列中第i個元素不為null,則遞迴呼叫insert方法,將part矩形插入到oct_values陣列中第i個元素所代表的子節點中 49 oct_values[i] ??= new OctTreeNode(part); // 如果oct_values陣列中第i個元素為null,則建立一個新的OctTreeNode物件,並賦值給oct_values陣列中第i個元素 50 } 51 } 52 } 53 54 public long area => origin.area + // 定義一個名為area的只讀屬性,表示當前節點所代表的矩形的面積加上所有子節點所代表的矩形的面積之和 55 oct_values.Sum(v => v?.area ?? 0); // 遍歷oct_values陣列中的每個元素,如果元素不為null,則獲取它所代表的矩形的面積,否則返回0 56 } 57 58 public static class Edm // 定義一個名為Edm的靜態類 59 { 60 public static long Calculate(IEnumerable<int[]> rc) // 定義一個名為Calculate的靜態方法,用於計算一組矩形的最大面積 61 { 62 var en = rc.Select(Rect.Create).GetEnumerator(); // 將傳入的矩形引數轉換為Rect物件,並獲取一個列舉器 63 if (!en.MoveNext()) return 0; // 如果列舉器沒有下一個元素,則直接返回0 64 65 var tree = new OctTreeNode(en.Current); // 建立一個新的OctTreeNode物件,並將列舉器的第一個元素作為引數傳入 66 while (en.MoveNext()) tree.insert(en.Current); // 遍歷列舉器中的每個元素,並將它們插入到tree中 67 return tree.area; // 返回tree所代表的矩形的面積 68 } 69 }
這段程式碼實現了一個八叉樹演算法,用於計算一組矩形的最大面積。
八叉樹演算法是一種經典的資料結構和演算法,其歷史可以追溯到20世紀60年代。它最早被用於計算機圖形學和計算機視覺領域,用於處理空間分割和區域查詢等問題。
八叉樹最早由法國電腦科學家Frits van der Hoeven在1966年引入,用於在計算機圖形學中進行空間分割和區域查詢。八叉樹的名字來源於其樹狀結構,每個節點有八個子節點,對應於三維空間中的八個象限。
隨後,八叉樹在計算機視覺領域得到廣泛應用,用於處理影像和空間資料。例如,八叉樹可以用於影像壓縮、影像搜尋、碰撞檢測等應用中。
隨著計算機硬體和演算法的發展,八叉樹的變種和改進也被提出。例如,四叉樹是八叉樹的二維版本,用於處理二維空間資料。此外,還有基於八叉樹的自適應分割方法和多解析度表示方法等。
八叉樹演算法的應用領域不僅限於計算機圖形學和計算機視覺,還可以用於地理資訊系統、醫學影像處理、物理模擬等領域。它提供了一種高效的資料結構和演算法,可以用於處理多維空間資料和進行空間查詢。
-
Rect
結構體表示一個矩形物件,其中包含矩形的左、右、上、下邊界,以及計算矩形面積和判斷矩形是否存在的屬性。 -
Rect
結構體還定義了一個intersects
方法,用於判斷兩個矩形是否相交,並返回相交部分的矩形物件。 -
Rect
結構體還定義了一個octants
方法,用於將當前矩形分成八個象限,並返回八個象限的矩形物件陣列。 -
OctTreeNode
類表示一個八叉樹節點,每個節點代表一個矩形。該類包含一個origin
變數表示當前節點所代表的矩形,以及一個oct_bounds
陣列表示當前節點所代表的矩形被分成的八個象限,以及一個oct_values
陣列表示八個象限所代表的子節點。 -
OctTreeNode
類定義了一個insert
方法,用於向當前節點插入一個新的矩形物件。該方法透過遍歷八個象限的矩形物件,判斷當前矩形與遍歷到的矩形是否相交,如果相交則遞迴呼叫insert
方法將相交部分的矩形插入到對應的子節點中。 -
OctTreeNode
類還定義了一個area
屬性,表示當前節點所代表的矩形的面積加上所有子節點所代表的矩形的面積之和。 -
Edm
靜態類定義了一個Calculate
方法,用於計算一組矩形的最大面積。該方法首先將傳入的矩形引數轉換為Rect
物件,並建立一個八叉樹節點。然後,遍歷矩形物件並將它們插入到八叉樹中。最後,返回八叉樹所代表的矩形的面積作為結果。
這個演算法的數學原理基於矩形的幾何性質和八叉樹的概念,透過構建八叉樹來處理矩形的相交和包含關係,最終得到一組矩形的最大面積並集。
演算法2中有些簡寫的cSharp語法,解釋如下:
1 //1.這段程式碼使用了C#語言的Lambda表示式和物件初始化器。Lambda表示式用於定義一個匿名方法,而物件初始化器用於在建立物件的同時對其屬性進行初始化。 2 public static Rect Create(params int[] c) => 3 new Rect() { l = c[0], b = c[1], r = c[2], t = c[3] }; 4 5 //這段程式碼定義了一個名為Create的靜態方法,其目的是建立一個矩形物件。該方法接受一個可變長度的整數陣列作為引數,陣列中的元素按照特定的順序表示矩形的左邊界、底邊界、右邊界和頂邊界。 6 //以下是另一種常用寫法: 7 public static Rect Create(params int[] c) 8 { 9 int left = c[0]; 10 int bottom = c[1]; 11 int right = c[2]; 12 int top = c[3]; 13 14 Rect rect = new Rect(); 15 rect.l = left; 16 rect.b = bottom; 17 rect.r = right; 18 rect.t = top; 19 20 return rect; 21 } 22 23 //2.同一 24 public bool intersects(Rect rc, out Rect result) => 25 (result = Create( 26 Math.Max(l, rc.l), Math.Max(b, rc.b), 27 Math.Min(r, rc.r), Math.Min(t, rc.t))) 28 .exist; 29 30 //常用寫法: 31 public bool Intersects(Rect rc, out Rect result) 32 { 33 int left = Math.Max(l, rc.l); 34 int bottom = Math.Max(b, rc.b); 35 int right = Math.Min(r, rc.r); 36 int top = Math.Min(t, rc.t); 37 38 result = Create(left, bottom, right, top); 39 40 return result.exist; 41 } 42 /* 43 在這個寫法中,首先根據兩個矩形的左邊界、底邊界、右邊界和頂邊界的最大值和最小值,分別計算出相交部分的矩形的左邊界、底邊界、右邊界和頂邊界。 44 45 然後,使用這些計算結果呼叫Create方法建立一個新的矩形物件,並將其賦值給result引數。 46 47 最後,返回新建立的矩形物件的exist屬性,表示兩個矩形是否相交。 48 49 這個寫法與原始程式碼的功能相同,但使用了更加明確的變數名和更加傳統的語法,使其更容易理解。*/
演算法2和演算法1都是用於計算矩形的面積交集的實現,但它們使用了不同的資料結構和演算法。
演算法1使用了一個二維陣列來表示每個點的覆蓋情況,並使用掃描線演算法來計算矩形的面積交集。這個演算法的優點是實現簡單,但需要額外的空間來儲存覆蓋情況,並且在處理大量矩形時可能會變得非常慢。
相比之下,演算法2使用了八叉樹的資料結構來表示矩形,並使用遞迴的方式來插入和查詢矩形。這個演算法的優點是可以高效地處理大量矩形,並且可以快速地計算矩形的面積交集。但缺點是實現相對複雜,需要額外的空間來儲存八叉樹節點,而且在處理高維資料時可能不太適用。
總的來說,這兩個演算法都有各自的優缺點,可以根據具體情況選擇適合的演算法。如果處理的資料量不是很大,或者需要實現的演算法比較簡單,那麼可以選擇演算法1;如果處理的資料量比較大,或者需要高效地計算矩形的面積交集,那麼可以選擇演算法2。
測試用例:
1 namespace Solution { 2 using NUnit.Framework; 3 using System; 4 using System.Collections.Generic; 5 using System.Linq; 6 using static NUnit.Framework.Assert; 7 8 9 public class RandSort 10 { 11 public static IEnumerable<int[]> Shuffle(List<int[]> recs) 12 { 13 var rnd = new Random(); 14 var order = new List<double>(); 15 for (int i = 0; i < recs.Count; i++) { 16 order.Add(rnd.NextDouble()); 17 } 18 var orderArray = order.ToArray(); 19 var recsArray = recs.ToArray(); 20 Array.Sort(orderArray, recsArray); 21 return recsArray; 22 } 23 } 24 25 [TestFixture] 26 public class BasicTests 27 { 28 [Test] 29 public void ZeroRectangles() 30 { 31 AreEqual(0, Edm.Calculate(Enumerable.Empty<int[]>())); 32 } 33 34 [Test] 35 public void OneRectangle() 36 { 37 AreEqual(1, Edm.Calculate(new [] { new [] {0,0,1,1}})); 38 } 39 40 [Test] 41 public void OneRectangleV2() 42 { 43 AreEqual(22, Edm.Calculate(new [] { new [] {0, 4, 11, 6}})); 44 } 45 46 [Test] 47 public void TwoRectangles() 48 { 49 AreEqual(2, Edm.Calculate(new [] { new [] {0,0,1,1}, new [] {1,1,2,2}})); 50 } 51 52 [Test] 53 public void TwoRectanglesV2() 54 { 55 AreEqual(4, Edm.Calculate(new [] { new [] {0,0,1,1}, new [] {0,0,2,2}})); 56 } 57 58 [Test] 59 public void ThreeRectangles() 60 { 61 AreEqual(36, Edm.Calculate(new [] { new [] {3,3,8,5}, new [] {6,3,8,9}, new [] {11,6,14,12}})); 62 } 63 } 64 65 [TestFixture] 66 public class ExpandedTests 67 { 68 [Test] 69 public void RectanglesWithoutIntersections() 70 { 71 var recs = new [] { 72 new [] { 1, 1, 2, 2 }, 73 new [] { 2, 2, 3, 3 }, 74 new [] { 3, 3, 4, 4 }, 75 new [] { 4, 4, 5, 5 }, 76 new [] { 2, 1, 3, 2 } 77 }; 78 79 AreEqual(5, Edm.Calculate(recs)); 80 } 81 82 83 [Test] 84 public void RectanglesWithSimpleIntersections() 85 { 86 var recs = new [] { 87 new [] { 1, 1, 2, 2 }, 88 new [] { 1, 4, 2, 7 }, 89 new [] { 1, 4, 2, 6 }, 90 new [] { 1, 4, 4, 5 }, 91 new [] { 2, 5, 6, 7 }, 92 new [] { 4, 3, 7, 6 }, 93 }; 94 95 AreEqual(21, Edm.Calculate(recs)); 96 } 97 98 [Test] 99 public void RectanglesWithSimpleIntersectionsV2() 100 { 101 var recs = new [] { 102 new [] { 1, 3, 4, 5 }, 103 new [] { 2, 1, 4, 7 }, 104 new [] { 3, 4, 5, 6 }, 105 new [] { 6, 6, 8, 7 }, 106 new [] { 5, 3, 8, 4 }, 107 new [] { 6, 0, 7, 3 }, 108 }; 109 110 AreEqual(24, Edm.Calculate(recs)); 111 } 112 113 [Test] 114 public void DifficultCommonFaces() 115 { 116 var rnd = new Random(); 117 int stepX = rnd.Next(10,20); 118 int stepY = rnd.Next(10,20); 119 int startX = rnd.Next(0, 1000); 120 int startY = rnd.Next(0, 1000); 121 int count = rnd.Next(1000, 1500); 122 123 var recs = new List<int[]>(); 124 125 for (var i = 0; i < count; i++) 126 { 127 var x = startX + i * stepX; 128 var y = startY + i * stepY; 129 recs.Add(new [] { x, y, x + 1, y + 1 }); 130 recs.Add(new [] { x + 1, y, x + 3, y + 2 }); 131 recs.Add(new [] { x, y + 2, x + 3, y + 3 }); 132 recs.Add(new [] { x + 3, y, x + 4, y + 3 }); 133 recs.Add(new [] { x + 2, y + 3, x + 4, y + 5 }); 134 } 135 136 var recsArray = RandSort.Shuffle(recs); 137 AreEqual(15 * count, Edm.Calculate(recsArray)); 138 } 139 140 141 [Test] 142 public void DifficultLocatedFarAway() 143 { 144 var rnd = new Random(); 145 int stepX = rnd.Next(1000,2000); 146 int stepY = rnd.Next(1000,2000); 147 int startX = rnd.Next(0, 1000); 148 int startY = rnd.Next(0, 1000); 149 int count = rnd.Next(1000, 1500); 150 151 var recs = new List<int[]>(); 152 153 for (var i = 0; i < count; i++) 154 { 155 var x = startX + i * stepX; 156 var y = startY + i * stepY; 157 recs.Add(new [] { x, y, x + 202, y + 300 }); 158 recs.Add(new [] { x + 100, y + 500, x + 500, y + 765 }); 159 recs.Add(new [] { x + 150, y + 330, x + 170, y + 360 }); 160 } 161 162 var recsArray = RandSort.Shuffle(recs); 163 AreEqual(167200 * count, Edm.Calculate(recsArray)); 164 } 165 166 167 [Test] 168 public void DifficultNestedRectangles() 169 { 170 var rnd = new Random(); 171 int stepX = rnd.Next(10,200); 172 int stepY = rnd.Next(10,200); 173 int startX = rnd.Next(0, 1000); 174 int startY = rnd.Next(0, 1000); 175 int count = rnd.Next(1000, 1500); 176 177 var recs = new List<int[]>(); 178 179 for (var i = 0; i < count; i++) 180 { 181 var x = startX + i * stepX; 182 var y = startY + i * stepY; 183 184 recs.Add(new [] { x, y, x + 1, y + 1 }); 185 recs.Add(new [] { x, y, x + 1, y + 3 }); 186 recs.Add(new [] { x, y + 1, x + 3, y + 2 }); 187 recs.Add(new [] { x, y + 3, x + 4, y + 4 }); 188 recs.Add(new [] { x + 2, y, x + 6, y + 2 }); 189 recs.Add(new [] { x + 3, y + 3, x + 6, y + 5 }); 190 } 191 192 var recsArray = RandSort.Shuffle(recs); 193 AreEqual(21 * count, Edm.Calculate(recsArray)); 194 } 195 196 197 [Test] 198 public void DifficultRectanglesWithLotsOfIntersections() 199 { 200 var rnd = new Random(); 201 int stepX = rnd.Next(10,200); 202 int stepY = rnd.Next(10,200); 203 int startX = rnd.Next(0, 1000); 204 int startY = rnd.Next(0, 1000); 205 int count = rnd.Next(1000, 1500); 206 207 var recs = new List<int[]>(); 208 209 for (var i = 0; i < count; i++) 210 { 211 var x = startX + i * stepX; 212 var y = startY + i * stepY; 213 214 recs.Add(new [] { x, y + 2, x + 2, y + 4 }); 215 recs.Add(new [] { x + 1, y + 3, x + 3, y + 5 }); 216 recs.Add(new [] { x + 1, y + 1, x + 3, y + 3 }); 217 recs.Add(new [] { x + 7, y + 3, x + 8, y + 4 }); 218 recs.Add(new [] { x + 8, y + 2, x + 9, y + 7 }); 219 recs.Add(new [] { x + 6, y + 2, x + 9, y + 7 }); 220 recs.Add(new [] { x + 3, y + 5, x + 10,y + 6 }); 221 recs.Add(new [] { x + 3, y + 2, x + 6, y + 3 }); 222 recs.Add(new [] { x + 2, y + 4, x + 4, y + 7 }); 223 recs.Add(new [] { x + 9, y, x + 10,y + 3 }); 224 } 225 226 var recsArray = RandSort.Shuffle(recs); 227 AreEqual(39 * count, Edm.Calculate(recsArray)); 228 } 229 230 231 [Test] 232 public void DifficultRectanglesWithLongSides() 233 { 234 var rnd = new Random(); 235 int stepX = rnd.Next(100000,111000); 236 int stepY = rnd.Next(100000,111000); 237 int startX = rnd.Next(0, 1000); 238 int startY = rnd.Next(0, 1000); 239 int count = rnd.Next(1000, 1500); 240 241 var recs = new List<int[]>(); 242 243 for (var i = 0; i < count; i++) 244 { 245 var x = startX + i * stepX; 246 var y = startY + i * stepY; 247 248 recs.Add(new [] { x, y, x + 30000, y + 1 }); 249 recs.Add(new [] { x, y + 1, x + 1, y + 30001 }); 250 recs.Add(new [] { x + 30000, y + 1, x + 30001, y + 30001 }); 251 } 252 253 var recsArray = RandSort.Shuffle(recs); 254 AreEqual(90000 * count, Edm.Calculate(recsArray)); 255 } 256 257 258 [Test] 259 public void DifficultRectanglesWithCommonFacesV2() 260 { 261 var rnd = new Random(); 262 int stepX = 0; //rnd.Next(100000,111000); 263 int stepY = rnd.Next(10,200); 264 int startX = rnd.Next(0, 1000); 265 int startY = rnd.Next(0, 1000); 266 int count = rnd.Next(1000, 1500); 267 268 var recs = new List<int[]>(); 269 270 for (var i = 0; i < count; i++) 271 { 272 var x = startX + i * stepX; 273 var y = startY + i * stepY; 274 275 recs.Add(new [] { x, y, x + 1, y + 1 }); 276 recs.Add(new [] { x + 1, y, x + 3, y + 2 }); 277 recs.Add(new [] { x, y + 2, x + 3, y + 3 }); 278 recs.Add(new [] { x + 3, y, x + 4, y + 3 }); 279 recs.Add(new [] { x + 2, y + 3, x + 4, y + 5 }); 280 } 281 282 var recsArray = RandSort.Shuffle(recs); 283 AreEqual(15 * count, Edm.Calculate(recsArray)); 284 } 285 286 287 [Test] 288 public void DifficultRectanglesLocatedFarAwayV2() 289 { 290 var rnd = new Random(); 291 int stepX = 0; //rnd.Next(100000,111000); 292 int stepY = rnd.Next(1000,2000); 293 int startX = rnd.Next(0, 1100); 294 int startY = rnd.Next(0, 1100); 295 int count = rnd.Next(1000, 1500); 296 297 var recs = new List<int[]>(); 298 299 for (var i = 0; i < count; i++) 300 { 301 var x = startX + i * stepX; 302 var y = startY + i * stepY; 303 304 recs.Add(new [] { x, y, x + 202, y + 300 }); 305 recs.Add(new [] { x + 100, y + 500, x + 500, y + 765 }); 306 recs.Add(new [] { x + 150, y + 330, x + 170, y + 360 }); 307 } 308 309 var recsArray = RandSort.Shuffle(recs); 310 AreEqual(167200 * count, Edm.Calculate(recsArray)); 311 } 312 313 314 [Test] 315 public void DifficultNestedRectanglesV2() 316 { 317 var rnd = new Random(); 318 int stepX = 0; //rnd.Next(100000,111000); 319 int stepY = rnd.Next(10,200); 320 int startX = rnd.Next(0, 1100); 321 int startY = rnd.Next(0, 1100); 322 int count = rnd.Next(1000, 1500); 323 324 var recs = new List<int[]>(); 325 326 for (var i = 0; i < count; i++) 327 { 328 var x = startX + i * stepX; 329 var y = startY + i * stepY; 330 331 recs.Add(new [] { x, y, x + 1, y + 1 }); 332 recs.Add(new [] { x, y, x + 1, y + 3 }); 333 recs.Add(new [] { x, y + 1, x + 3, y + 2 }); 334 recs.Add(new [] { x, y + 3, x + 4, y + 4 }); 335 recs.Add(new [] { x + 2, y, x + 6, y + 2 }); 336 recs.Add(new [] { x + 3, y + 3, x + 6, y + 5 }); 337 } 338 339 var recsArray = RandSort.Shuffle(recs); 340 AreEqual(21 * count, Edm.Calculate(recsArray)); 341 } 342 343 [Test] 344 public void DifficultRectanglesWithLotsOfIntersectionsV2() 345 { 346 var rnd = new Random(); 347 int stepX = 0; //rnd.Next(100000,111000); 348 int stepY = rnd.Next(10,200); 349 int startX = rnd.Next(0, 1100); 350 int startY = rnd.Next(0, 1100); 351 int count = rnd.Next(1000, 1500); 352 353 var recs = new List<int[]>(); 354 355 for (var i = 0; i < count; i++) 356 { 357 var x = startX + i * stepX; 358 var y = startY + i * stepY; 359 360 recs.Add(new [] { x, y + 2, x + 2, y + 4 }); 361 recs.Add(new [] { x + 1, y + 3, x + 3, y + 5 }); 362 recs.Add(new [] { x + 1, y + 1, x + 3, y + 3 }); 363 recs.Add(new [] { x + 7, y + 3, x + 8, y + 4 }); 364 recs.Add(new [] { x + 8, y + 2, x + 9, y + 7 }); 365 recs.Add(new [] { x + 6, y + 2, x + 9, y + 7 }); 366 recs.Add(new [] { x + 3, y + 5, x + 10,y + 6 }); 367 recs.Add(new [] { x + 3, y + 2, x + 6, y + 3 }); 368 recs.Add(new [] { x + 2, y + 4, x + 4, y + 7 }); 369 recs.Add(new [] { x + 9, y, x + 10,y + 3 }); 370 } 371 372 var recsArray = RandSort.Shuffle(recs); 373 AreEqual(39 * count, Edm.Calculate(recsArray)); 374 } 375 376 377 [Test] 378 public void DifficultRectanglesWithLongSidesV2() 379 { 380 var rnd = new Random(); 381 int stepX = 0; //rnd.Next(100000,111000); 382 int stepY = rnd.Next(100000,111200); 383 int startX = rnd.Next(0, 1100); 384 int startY = rnd.Next(0, 1100); 385 int count = rnd.Next(1000, 1500); 386 387 var recs = new List<int[]>(); 388 389 for (var i = 0; i < count; i++) 390 { 391 var x = startX + i * stepX; 392 var y = startY + i * stepY; 393 394 recs.Add(new [] { x, y, x + 30000, y + 1 }); 395 recs.Add(new [] { x, y + 1, x + 1, y + 30001 }); 396 recs.Add(new [] { x + 30000, y + 1, x + 30001, y + 30001 }); 397 } 398 399 var recsArray = RandSort.Shuffle(recs); 400 AreEqual(90000 * count, Edm.Calculate(recsArray)); 401 } 402 403 private static long Solve(IEnumerable<int[]> rectangles) 404 { 405 if (!rectangles.Any()) return 0; 406 rectangles = rectangles.OrderBy(r => r[0]).ToList(); 407 var xs = rectangles.Select(r=>r[0]).Concat(rectangles.Select(r=>r[2])).Distinct().OrderBy(x=>x).ToList(); 408 var scan = new List<int[]>(); 409 // long recIndex = 0; 410 long area = 0; 411 long scanLeft = xs[0]; 412 xs.RemoveAt(0); 413 using(var recsEnum = rectangles.GetEnumerator()) 414 { 415 bool hasMoreRec = recsEnum.MoveNext(); 416 417 xs.ForEach(scanRight => 418 { 419 // add rectangles to scan that align on scan left... 420 for(;hasMoreRec && recsEnum.Current[0] == scanLeft; hasMoreRec = recsEnum.MoveNext()) 421 { 422 scan.Add(recsEnum.Current); 423 } 424 425 scan.Sort((a,b) => a[1] - b[1]); // order by top 426 long height = 0; 427 long lastY = long.MinValue; // last y accounted for in height 428 scan.ForEach(s => 429 { 430 long top = s[1]; 431 long bottom = s[3]; 432 if (lastY < bottom) // overlaps, add height of overlapping portion 433 { 434 height += bottom - Math.Max(lastY, top); 435 lastY = bottom; 436 } 437 }); 438 439 // area of rectangles that overlap scan: height * width 440 area += height * (scanRight - scanLeft); 441 442 // proceding left-to-right, so remove the scan rectangles whose right-side is to the left of current scan 443 scan.RemoveAll(r=>r[2] <= scanRight); 444 scanLeft = scanRight; 445 }); 446 } 447 448 return area; 449 } 450 451 [Test] 452 public void DifficultRandomTests() 453 { 454 const int scale = 100000; 455 var rnd = new Random(); 456 for(int i=0; i<25; i++) 457 { 458 int sx=rnd.Next(0,scale); 459 int sy=rnd.Next(0,scale); 460 var recs = Enumerable.Range(0,rnd.Next(300,500)).Select(_ => new [] { sx, sy, sx + rnd.Next(0,scale), sy + rnd.Next(0, scale) }).ToArray(); 461 var expected = Solve(recs); 462 var actual = Edm.Calculate(recs); 463 AreEqual(expected, actual); 464 } 465 } 466 } 467 }