Merge pull request '2025-09/3027-find-the-number-of-ways-to-place-people-ii' (#7) from 2025-09/3027-find-the-number-of-ways-to-place-people-ii into main

Reviewed-on: #7
This commit is contained in:
2025-09-03 03:06:42 +00:00
6 changed files with 509 additions and 0 deletions

View File

@@ -0,0 +1,68 @@
# [3027] Find The Number Of Ways To Place People II
## 題目資訊
- **難度**: Hard
- **標籤**: Array, Math, Geometry, Enumeration
- **題目連結**: [LeetCode](https://leetcode.com/problems/find-the-number-of-ways-to-place-people-ii/)
- **練習日期**: 2025-09-03
## 題目描述
You are given a 2D array `points` of size `n x 2` representing integer coordinates of some points on a 2D-plane, where `points[i] = [xi, yi]`.
We define the **right** direction as positive x-axis **(increasing x-coordinate)** and the **left** direction as negative x-axis **(decreasing x-coordinate)**. Similarly, we define the **up** direction as positive y-axis **(increasing y-coordinate)** and the **down** direction as negative y-axis **(decreasing y-coordinate)**
You have to place `n` people, including Alice and Bob, at these points such that there is **exactly one** person at every point. Alice wants to be alone with Bob, so Alice will build a rectangular fence with Alice's position as the **upper left corner** and Bob's position as the **lower right corner** of the fence (**Note** that the fence **might not** enclose any area, i.e. it can be a line). If any person other than Alice and Bob is either **inside** the fence or on the fence, Alice will be sad.
Return the number of *pairs of points* where you can place Alice and Bob, such that Alice *does not* become sad on building the fence.
**Note** that Alice can only build a fence with Alice's position as the upper left corner, and Bob's position as the lower right corner. For example, Alice cannot build either of the fences in the picture below with four corners `(1, 1)`, `(1, 3)`, `(3, 1)`, and `(3, 3)`, because:
- With Alice at `(3, 3)` and Bob at `(1, 1)`, Alice's position is not the upper left corner and Bob's position is not the lower right corner of the fence.
- With Alice at `(1, 3)` and Bob at `(1, 1)`, Bob's position is not the lower right corner of the fence.
## 解題思路
### 初步分析
此題同[#3025](../3025-find-the-number-of-ways-to-place-people-i/README.md)
### 解法概述
此題同[#3025](../3025-find-the-number-of-ways-to-place-people-i/README.md)
## 測試案例
### 範例輸入輸出
```
Input: points = [[6,2],[4,4],[2,6]]
Output: 2
Explanation:
There are two ways to place Alice and Bob such that Alice will not be sad:
- Place Alice at (4, 4) and Bob at (6, 2).
- Place Alice at (2, 6) and Bob at (4, 4).
You cannot place Alice at (2, 6) and Bob at (6, 2) because the person at (4, 4) will be inside the fence.
```
### 邊界情況
- 2 <= n <= 1000
- points[i].length == 2
- -10^9 <= points[i][0], points[i][1] <= 10^9
- All points[i] are distinct.
## 學習筆記
### 今天學到什麼?
- 跟 [#3025]一樣, 但多了更多描述, 可能英文要再加強(?)
- 多了更大的邊界條件, 寫unit test 需要考慮更多覆蓋率
### 遇到的困難
-
### 改善方向
-
### 相關題目
- [#223](https://leetcode.com/problems/rectangle-area/) Rectangle Area
---
**總結**: 這題的核心概念是...,適合練習...技巧。

View File

@@ -0,0 +1,76 @@
// LeetCode 3027: Find The Number Of Ways To Place People Ii
// 難度: Hard
// 日期: 2025-09-03
using System;
using System.Collections.Generic;
using System.Linq;
public class Solution {
public int NumberOfPairs(int[][] points)
{
int n = points.Length;
int result = 0;
for (var i = 0; i < n; i++)
{
for (var j = 0; j < n; j++)
{
if (i == j) continue;
// 判斷 Alice points[i] 是否在 Bob points[j] 左上角
var Alice = points[i];
var Bob = points[j];
if (Alice[0] <= Bob[0] && Alice[1] >= Bob[1])
{
// get border
int minX = Alice[0];
int maxX = Bob[0];
int minY = Bob[1];
int maxY = Alice[1];
bool isAnyPerson = false;
for (var k = 0; k < n; k++)
{
if (i == k || j == k) continue;
var person = points[k];
if (minX <= person[0] && person[0] <= maxX && minY <= person[1] && person[1] <= maxY)
{
isAnyPerson = true;
break;
}
}
if (!isAnyPerson) result++;
}
}
}
return result;
}
}
public class Program {
public static void Main() {
Solution solution = new Solution();
// 測試案例
TestCase(solution);
}
static void TestCase(Solution solution) {
// Input:
int[][] points = new int[][] {
new int[] {6, 2},
new int[] {4, 4},
new int[] {2, 6}
};
// Expected:
Console.WriteLine($"Test Case: [[6,2],[4,4],[2,6]]");
Console.WriteLine($"Expected: 2");
// Actual:
int result1 = solution.NumberOfPairs(points);
Console.WriteLine($"Result: {result1}");
}
}

View File

@@ -0,0 +1,10 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>

View File

@@ -0,0 +1,313 @@
// LeetCode 3027 單元測試
// 使用 xUnit 框架
using Xunit;
using System;
using System.Collections.Generic;
public class SolutionTests
{
private readonly Solution _solution;
public SolutionTests()
{
_solution = new Solution();
}
[Fact]
public void TestCase1_ExampleFromProblem()
{
// Arrange
int[][] points = new int[][] {
new int[] {6, 2},
new int[] {4, 4},
new int[] {2, 6}
};
int expected = 2;
// Act
var result = _solution.NumberOfPairs(points);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void TestCase2_TwoPoints()
{
// Arrange
int[][] points = new int[][] {
new int[] {1, 3},
new int[] {2, 1}
};
int expected = 1;
// Act
var result = _solution.NumberOfPairs(points);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void TestCase3_NoValidPairs()
{
// Arrange - 沒有有效點對的情況
int[][] points = new int[][] {
new int[] {1, 1},
new int[] {2, 2},
new int[] {3, 3}
};
int expected = 0;
// Act
var result = _solution.NumberOfPairs(points);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void TestCase4_SameXCoordinate()
{
// Arrange - x 座標相同的情況
int[][] points = new int[][] {
new int[] {3, 5},
new int[] {3, 2},
new int[] {3, 1}
};
int expected = 2;
// Act
var result = _solution.NumberOfPairs(points);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void TestCase5_SameYCoordinate()
{
// Arrange - y 座標相同的情況
int[][] points = new int[][] {
new int[] {1, 4},
new int[] {3, 4},
new int[] {5, 4}
};
int expected = 2;
// Act
var result = _solution.NumberOfPairs(points);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void TestCase6_PointsInsideRectangle()
{
// Arrange - 矩形內有其他點的情況
int[][] points = new int[][] {
new int[] {0, 4}, // A
new int[] {4, 0}, // B
new int[] {2, 2} // C (在 A-B 矩形內)
};
int expected = 2;
// Act
var result = _solution.NumberOfPairs(points);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void TestCase7_PointsOnBorder()
{
// Arrange - 點在矩形邊界上的情況
int[][] points = new int[][] {
new int[] {0, 3}, // A
new int[] {3, 0}, // B
new int[] {0, 0} // C (在矩形邊界上)
};
int expected = 2; // (A,C) 和 (C,B) 有效
// Act
var result = _solution.NumberOfPairs(points);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void TestCase8_NegativeCoordinates()
{
// Arrange - 負座標情況 (題目一允許 -10^9 到 10^9)
int[][] points = new int[][] {
new int[] {-5, 5},
new int[] {5, -5},
new int[] {0, 0}
};
int expected = 2; // (-5,5) -> (0,0) 和 (0,0) -> (5,-5) 有效
// Act
var result = _solution.NumberOfPairs(points);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void TestCase9_LargeCoordinates()
{
// Arrange - 大座標值情況
int[][] points = new int[][] {
new int[] {-1000000, 1000000},
new int[] {1000000, -1000000},
new int[] {0, 0}
};
int expected = 2;
// Act
var result = _solution.NumberOfPairs(points);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void TestEdgeCases_MinimumPoints()
{
// Arrange - 最少點數情況 (題目限制 n >= 2)
int[][] points = new int[][] {
new int[] {0, 1},
new int[] {1, 0}
};
int expected = 1; // (0,1) 在 (1,0) 的左上方
// Act
int result = _solution.NumberOfPairs(points);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void TestEdgeCases_MaximumCoordinateRange()
{
// Arrange - 最大座標範圍測試
int[][] points = new int[][] {
new int[] {-1000000000, 1000000000}, // 最小x最大y
new int[] {1000000000, -1000000000}, // 最大x最小y
new int[] {0, 0} // 中心點
};
int expected = 2;
// Act
int result = _solution.NumberOfPairs(points);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void TestCase_LargerDataSet()
{
// Arrange - 較大的數據集 (接近題目一的限制 n <= 1000)
var points = new List<int[]>();
// 創建 100 個不同的點 (可以增加到更接近 1000但為了測試速度這裡用 100)
for (int i = 0; i < 10; i++)
{
for (int j = 0; j < 10; j++)
{
points.Add(new int[] { i * 100 - 500, j * 100 - 500 });
}
}
int[][] pointsArray = points.ToArray();
// Act
int result = _solution.NumberOfPairs(pointsArray);
// Assert
Assert.True(result >= 0); // 結果應該是非負數
Assert.True(result <= 100 * 99); // 最多不會超過所有可能的點對
}
[Fact]
public void TestCase_StressTest_MediumSize()
{
// Arrange - 中等規模壓力測試 (200個點)
var points = new List<int[]>();
var usedPoints = new HashSet<string>();
var random = new Random(42); // 固定種子確保可重現
while (points.Count < 200)
{
int x = random.Next(-10000, 10001); // 較大範圍
int y = random.Next(-10000, 10001);
string pointKey = $"{x},{y}";
if (!usedPoints.Contains(pointKey))
{
usedPoints.Add(pointKey);
points.Add(new int[] { x, y });
}
}
int[][] pointsArray = points.ToArray();
// Act
var startTime = DateTime.Now;
int result = _solution.NumberOfPairs(pointsArray);
var endTime = DateTime.Now;
var elapsed = endTime - startTime;
// Assert
Assert.True(result >= 0);
Assert.True(result <= 200 * 199);
Assert.True(elapsed.TotalMilliseconds < 5000); // 應該在5秒內完成
}
[Fact]
public void TestCase_AllPointsOnSameLine_Vertical()
{
// Arrange - 所有點在同一條垂直線上
int[][] points = new int[][] {
new int[] {0, 10},
new int[] {0, 5},
new int[] {0, 0},
new int[] {0, -5},
new int[] {0, -10}
};
int expected = 4; // 每個點都可以與y座標更小的點配對
// Act
int result = _solution.NumberOfPairs(points);
// Assert
Assert.Equal(expected, result);
}
[Fact]
public void TestCase_AllPointsOnSameLine_Horizontal()
{
// Arrange - 所有點在同一條水平線上
int[][] points = new int[][] {
new int[] {-10, 0},
new int[] {-5, 0},
new int[] {0, 0},
new int[] {5, 0},
new int[] {10, 0}
};
int expected = 4; // 每個點都可以與x座標更大的點配對
// Act
int result = _solution.NumberOfPairs(points);
// Assert
Assert.Equal(expected, result);
}
}

View File

@@ -0,0 +1,22 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.1.0" />
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../csharp/csharp.csproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,20 @@
#!/bin/bash
echo "🧪 執行 LeetCode 題目測試"
echo "=========================="
# 執行 C# 測試
echo "📋 C# 測試結果:"
cd ../csharp
if dotnet build > /dev/null 2>&1; then
cd ../test
if dotnet test --logger "console;verbosity=minimal" 2>/dev/null; then
echo "✅ C# 測試通過"
else
echo "❌ C# 測試失敗"
fi
else
echo "❌ C# 編譯失敗"
fi
echo "📊 測試完成!"