From 30f9a73a17279f8fb89f7cc85c12e0cbe7014d9b Mon Sep 17 00:00:00 2001 From: MH Hung Date: Mon, 1 Sep 2025 10:03:54 +0800 Subject: [PATCH 1/6] fix(template): solved redundant filess --- leetcode_helper.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/leetcode_helper.sh b/leetcode_helper.sh index 5f00128..222b065 100755 --- a/leetcode_helper.sh +++ b/leetcode_helper.sh @@ -337,9 +337,9 @@ EOF # 建立 C# 專案 mkdir -p "$problem_dir/csharp" cd "$problem_dir/csharp" - dotnet new console -n "Problem$number" --force > /dev/null 2>&1 + dotnet new console --force > /dev/null 2>&1 - # 建立 C# 解法檔案 + # 覆寫自動生成的 Program.cs cat > "Program.cs" << EOF // LeetCode $number: $(echo "$name" | tr '-' ' ' | sed 's/\b\w/\u&/g') // 難度: $difficulty @@ -430,7 +430,7 @@ EOF echo "├── README.md" echo "├── csharp/" echo "│ ├── Program.cs" - echo "│ └── Problem$number.csproj" + echo "│ └── *.csproj" echo "├── go/" echo "│ ├── main.go" echo "│ └── go.mod" -- 2.49.1 From 7c9e454777e198e8af4e28740b88568d33653339 Mon Sep 17 00:00:00 2001 From: MH Hung Date: Mon, 1 Sep 2025 11:15:34 +0800 Subject: [PATCH 2/6] [#1792]feat(MaxAverageRatio) : add C# brute force solution --- .../1792-maximum-average-pass-ratio/README.md | 71 ++++++++++ .../csharp/Program.cs | 130 ++++++++++++++++++ .../csharp/csharp.csproj | 10 ++ 3 files changed, 211 insertions(+) create mode 100644 problems/1792-maximum-average-pass-ratio/README.md create mode 100644 problems/1792-maximum-average-pass-ratio/csharp/Program.cs create mode 100644 problems/1792-maximum-average-pass-ratio/csharp/csharp.csproj diff --git a/problems/1792-maximum-average-pass-ratio/README.md b/problems/1792-maximum-average-pass-ratio/README.md new file mode 100644 index 0000000..7a3e56b --- /dev/null +++ b/problems/1792-maximum-average-pass-ratio/README.md @@ -0,0 +1,71 @@ +# [1792] Maximum Average Pass Ratio + +## 題目資訊 +- **難度**: Medium +- **標籤**: Array, Greedy, Heap(Priority Queue) +- **題目連結**: [LeetCode](https://leetcode.com/problems/maximum-average-pass-ratio/) +- **練習日期**: 2025-09-01 + +## 題目描述 +There is a school that has classes of students and each class will be having a final exam. You are given a 2D integer array `classes`, where `classes[i] = [passi, totali]`. You know beforehand that in the `ith` class, there are `totali` total students, but only `passi` number of students will pass the exam. + +You are also given an integer `extraStudents`. There are another `extraStudents` brilliant students that are **guaranteed** to pass the exam of any class they are assigned to. You want to assign each of the `extraStudents` students to a class in a way that **maximizes** the **average** pass ratio across **all** the classes. + +The **pass ratio** of a class is equal to the number of students of the class that will pass the exam divided by the total number of students of the class. The **average pass ratio** is the sum of pass ratios of all the classes divided by the number of the classes. + +Return the *maximum* possible average pass ratio after assigning the `extraStudents` students. Answers within `10-5` of the actual answer will be accepted. + +## 解題思路 + +### 初步分析 +- 這題主要考察什麼概念? + - 貪心算法,找出每次分配學生,通過率可以提升至最高 +- Pass Ratio = pass / total +- Average Pass Ratio = sum of pass ration / number of class + +### 解法概述 +1. **暴力解法**: + - 思路: + - 每次分配一個學生看哪個班級提升率會最高 + - leetcode 這邊會 time limit exceeded + - 時間複雜度:O(N) + - 空間複雜度:O(N) + +2. **最佳解法**: + - 思路: + - Priority Queue(Max Heap) 存每個班級淺在 + - 時間複雜度:O(?) + - 空間複雜度:O(?) + +## 測試案例 + +### 範例輸入輸出 +``` +Input: classes = [[1,2],[3,5],[2,2]], extraStudents = 2 +Output: 0.78333 +Explanation: You can assign the two extra students to the first class. The average pass ratio will be equal to (3/4 + 3/5 + 2/2) / 3 = 0.78333. +``` + +### 邊界情況 +- 1 <= classes.length <= 10^5 +- classes[i].length == 2 +- 1 <= passi <= totali <= 10^5 +- 1 <= extraStudents <= 10^5 + +## 學習筆記 + +### 今天學到什麼? +- + +### 遇到的困難 +- + +### 改善方向 +- + +### 相關題目 +- [題目編號] 題目名稱 - 相似概念 +- [題目編號] 題目名稱 - 進階版本 + +--- +**總結**: 這題的核心概念是...,適合練習...技巧。 diff --git a/problems/1792-maximum-average-pass-ratio/csharp/Program.cs b/problems/1792-maximum-average-pass-ratio/csharp/Program.cs new file mode 100644 index 0000000..bd54a7b --- /dev/null +++ b/problems/1792-maximum-average-pass-ratio/csharp/Program.cs @@ -0,0 +1,130 @@ +// LeetCode 1792: Maximum Average Pass Ratio +// 難度: Medium +// 日期: 2025-09-01 + +using System; +using System.Collections.Generic; +using System.Linq; + +public class Solution { + public double MaxAverageRatioByBruteForce(int[][] classes, int extraStudents) + { + var n = classes.Length; + var pass = new int[n]; + var total = new int[n]; + + for (var i = 0; i < n; i++) + { + pass[i] = classes[i][0]; + total[i] = classes[i][1]; + } + + for (int student = 0; student < extraStudents; student++) + { + int bestClass = -1; + double maxInprovement = 0; + + for (int i = 0; i < n; i++) + { + double currentAvg = CalculateAvergaeRatio(pass, total); + + pass[i]++; + total[i]++; + + double newAvg = CalculateAvergaeRatio(pass, total); + double improvement = newAvg - currentAvg; + + pass[i]--; + total[i]--; + + if (improvement > maxInprovement) + { + maxInprovement = improvement; + bestClass = i; + } + } + + if (bestClass != -1) + { + pass[bestClass]++; + total[bestClass]++; + } + } + + return CalculateAvergaeRatio(pass, total); + } + + private double CalculateAvergaeRatio(int[] pass, int[] total) { + double sum = 0; + for (var i = 0; i < pass.Length; i++) + { + sum += (double)pass[i] / total[i]; + } + return sum / pass.Length; + } +} + +// 測試程式 +public class Program +{ + public static void Main() + { + Solution solution = new Solution(); + + // TestCase + TestCase1(solution); + TestCase2(solution); + } + + static void TestCase1(Solution solution) + { + // Input: + int[][] classes = new int[][] + { + new int[] {1, 2}, + new int[] {3, 5}, + new int[] {2, 2} + }; + int extraStudents = 2; + + Console.WriteLine("Test 1: "); + Console.WriteLine($"classes: [{string.Join("], [", classes.Select(c => string.Join(", ", c)))}]"); + Console.WriteLine($"extraStudents: {extraStudents}"); + + // Expected: + Console.WriteLine("Expect: "); + Console.WriteLine($"output: 0.78333"); + + // Actual: + Console.WriteLine("Actual: "); + Console.WriteLine($"Brute Force: {solution.MaxAverageRatioByBruteForce(classes, extraStudents):F6}"); + //Console.WriteLine($"output: {solution.MaxAverageRatio(classes, extraStudents):F6}"); + } + + + static void TestCase2(Solution solution) + { + // Input: + int[][] classes = new int[][] + { + new int[] {2, 4}, + new int[] {3, 9}, + new int[] {4, 5}, + new int[] {2, 10} + }; + int extraStudents = 4; + + Console.WriteLine("Test 2: "); + Console.WriteLine($"classes: [{string.Join("], [", classes.Select(c => string.Join(", ", c)))}]"); + Console.WriteLine($"extraStudents: {extraStudents}"); + + // Expected: + Console.WriteLine("Expect: "); + Console.WriteLine($"output: 0.53485"); + + // Actual: + Console.WriteLine("Actual: "); + Console.WriteLine($"Brute Force: {solution.MaxAverageRatioByBruteForce(classes, extraStudents):F6}"); + //Console.WriteLine($"output: {solution.MaxAverageRatio(classes, extraStudents):F6}"); + } +} diff --git a/problems/1792-maximum-average-pass-ratio/csharp/csharp.csproj b/problems/1792-maximum-average-pass-ratio/csharp/csharp.csproj new file mode 100644 index 0000000..2150e37 --- /dev/null +++ b/problems/1792-maximum-average-pass-ratio/csharp/csharp.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + -- 2.49.1 From 24828dfbf089df37492f8386a74cd05e07d40a0c Mon Sep 17 00:00:00 2001 From: MH Hung Date: Mon, 1 Sep 2025 13:38:07 +0800 Subject: [PATCH 3/6] [#1782] test(MaxAverageRatio): Add test cases for C# solution --- .../test/SolutionTests.cs | 176 ++++++++++++++++++ .../test/TestProject.csproj | 22 +++ 2 files changed, 198 insertions(+) create mode 100644 problems/1792-maximum-average-pass-ratio/test/SolutionTests.cs create mode 100644 problems/1792-maximum-average-pass-ratio/test/TestProject.csproj diff --git a/problems/1792-maximum-average-pass-ratio/test/SolutionTests.cs b/problems/1792-maximum-average-pass-ratio/test/SolutionTests.cs new file mode 100644 index 0000000..3a89a51 --- /dev/null +++ b/problems/1792-maximum-average-pass-ratio/test/SolutionTests.cs @@ -0,0 +1,176 @@ +// LeetCode 1792 單元測試 +// 使用 xUnit 框架 + +using Xunit; +using System; +using System.Collections.Generic; + +public class SolutionTests +{ + private readonly Solution _solution; + private const double TOLERANCE = 0.00001; + + public SolutionTests() + { + _solution = new Solution(); + } + + #region Basic Test + [Fact] + public void TestCase1() + { + // Arrange + int[][] classes = new int[][] + { + new int[] {1, 2}, + new int[] {3, 5}, + new int[] {2, 2} + }; + int extraStudents = 2; + double expected = 0.78333; + + // Act + var result = _solution.MaxAverageRatio(classes, extraStudents); + + // Assert + Assert.True(Math.Abs(result - expected) < TOLERANCE, + $"Expected: {expected}, Actual: {result}"); + } + + [Fact] + public void TestCase2() + { + + // Arrange + int[][] classes = new int[][] + { + new int[] {2, 4}, // 通過率: 2/4 = 0.5 + new int[] {3, 9}, // 通過率: 3/9 = 0.333... + new int[] {4, 5}, // 通過率: 4/5 = 0.8 + new int[] {2, 10} // 通過率: 2/10 = 0.2 + }; + int extraStudents = 4; + double expected = 0.53485; + + // Act + var result = _solution.MaxAverageRatio(classes, extraStudents); + + // Assert + Assert.True(Math.Abs(result - expected) < TOLERANCE, + $"Expected: {expected}, Actual: {result}"); + } + + [Fact] + public void TestBruteForceVsPriorityQueue() + { + // Arrange + int[][] classes = new int[][] + { + new int[] {1, 3}, + new int[] {2, 4}, + new int[] {1, 5} + }; + int extraStudents = 3; + + // Act + var resultBruteForce = _solution.MaxAverageRatioByBruteForce(classes, extraStudents); + var resultPriorityQueue = _solution.MaxAverageRatio(classes, extraStudents); + + // Assert + Assert.True(Math.Abs(resultBruteForce - resultPriorityQueue) < TOLERANCE, + $"Brute Force: {resultBruteForce}, Priority Queue: {resultPriorityQueue}"); + } + #endregion + + #region Edge Test + [Fact] + public void TestEdgeCases_MinClassesLength_OneClass() + { + // Arrange + int[][] classes = new int[][] + { + new int[] {50, 100} + }; + int extraStudents = 10; + double expected = 60.0 / 110.0; + + // Act + var result = _solution.MaxAverageRatio(classes, extraStudents); + + // Assert + Assert.True(result >= 0 && result <= 1, + $"Result should be between 0 and 1, got: {result}"); + Assert.True(Math.Abs(result - expected) < TOLERANCE, + $"Expected: {expected}, Actual: {result}"); + } + + [Fact] + public void TestEdgeCases_LargeClassesLength() + { + // Arrange + const int numClasses = 1000; + var classes = new int[numClasses][]; + var random = new Random(42); + + for (int i = 0; i < numClasses; i++) + { + int total = random.Next(10, 100); // total 在 10-100 之間 + int pass = random.Next(1, total + 1); // pass 在 1-total 之間 + classes[i] = new int[] { pass, total }; + } + int extraStudents = 100; + + // Act + var result = _solution.MaxAverageRatio(classes, extraStudents); + + // Assert + Assert.True(result >= 0 && result <= 1, + $"Result should be between 0 and 1, got: {result}"); + } + + [Fact] + public void TestEdgeCases_MinExtraStudents_One() + { + // Arrange + int[][] classes = new int[][] + { + new int[] {1, 3}, + new int[] {2, 4} + }; + int extraStudents = 1; + + // Act + var result = _solution.MaxAverageRatio(classes, extraStudents); + + // Assert + double originalAvg = (1.0 / 3 + 2.0 / 4) / 2; + Assert.True(result > originalAvg, + $"Result {result} should be greater than original average {originalAvg}"); + } + + [Fact] + public void TestEdgeCases_LargeExtraStudents() + { + // Arrange - 大量額外學生 + int[][] classes = new int[][] + { + new int[] {10, 100}, // 10% 通過率 + new int[] {20, 100}, // 20% 通過率 + new int[] {30, 100} // 30% 通過率 + }; + int extraStudents = 10000; + + // Act + var result = _solution.MaxAverageRatio(classes, extraStudents); + + // Assert + Assert.True(result >= 0 && result <= 1, + $"Result should be between 0 and 1, got: {result}"); + + // 大量學生加入後,通過率應該顯著提升 + double originalAvg = (0.1 + 0.2 + 0.3) / 3; // = 0.2 + Assert.True(result > originalAvg + 0.1, + $"Result {result} should be significantly higher than original average {originalAvg}"); + } + #endregion +} \ No newline at end of file diff --git a/problems/1792-maximum-average-pass-ratio/test/TestProject.csproj b/problems/1792-maximum-average-pass-ratio/test/TestProject.csproj new file mode 100644 index 0000000..2f44c14 --- /dev/null +++ b/problems/1792-maximum-average-pass-ratio/test/TestProject.csproj @@ -0,0 +1,22 @@ + + + + net8.0 + enable + false + + + + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + + + + + -- 2.49.1 From 70f64e1ef7ee1082e137e0f6acc9881e0941f5e0 Mon Sep 17 00:00:00 2001 From: MH Hung Date: Mon, 1 Sep 2025 13:44:57 +0800 Subject: [PATCH 4/6] [#1782] note(MaxAverageRatio): Add solution README --- logs/2025-09.md | 69 +++++++++++++++++++ .../1792-maximum-average-pass-ratio/README.md | 16 +++-- 2 files changed, 79 insertions(+), 6 deletions(-) diff --git a/logs/2025-09.md b/logs/2025-09.md index e69de29..7f7c42e 100644 --- a/logs/2025-09.md +++ b/logs/2025-09.md @@ -0,0 +1,69 @@ +# 2025-09 學習記錄 + +## 📅 每日練習記錄 + +### Week 1 +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| 09-01 | Maximum Average Pass Ratio | Medium | C# | 1hr | Done | 第一次用到Priority Queue | + +### Week 2 +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 3 +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +### Week 4 +| 日期 | 題目 | 難度 | 語言 | 耗時 | 狀態 | 心得 | +|------|------|------|------|------|------|------| +| | | | | | | | + +## 📈 本月統計 + +### 完成情況 +- **總練習天數**: 1 天 +- **完成題數**: 1題 +- **語言分布**: C# 1(題), Go 0(題) +- **難度分布**: Easy 0(題), Medium 1(題), Hard 0(題) + +### 時間投入 +- **總時間**: 1小時 +- **平均每題**: 分鐘 +- **每日平均**: 分鐘 + +## 🎯 本月重點學習 + +### 新掌握的技巧 +1. 貪心策略 + +### 常犯錯誤分析 +1. + +### 語言學習心得 +- **C#**: +- **Go**: + +## 🔄 遇到的困難與解決 + +### 困難1: +- **問題**: +- **解決**: +- **學習**: + +## 📝 改進計畫 + +### 下月目標 +1. + +### 學習方法調整 +1. + +## 💡 本月金句 +> + +--- +**總結**: diff --git a/problems/1792-maximum-average-pass-ratio/README.md b/problems/1792-maximum-average-pass-ratio/README.md index 7a3e56b..ba2172d 100644 --- a/problems/1792-maximum-average-pass-ratio/README.md +++ b/problems/1792-maximum-average-pass-ratio/README.md @@ -33,7 +33,7 @@ Return the *maximum* possible average pass ratio after assigning the `extraStude 2. **最佳解法**: - 思路: - - Priority Queue(Max Heap) 存每個班級淺在 + - Priority Queue(Max Heap) 存每個班級潛在 - 時間複雜度:O(?) - 空間複雜度:O(?) @@ -55,17 +55,21 @@ Explanation: You can assign the two extra students to the first class. The avera ## 學習筆記 ### 今天學到什麼? -- +- 第一次使用 Priority Queue +- 第一次使用 Unit Test ### 遇到的困難 -- +- 可能要再了解 Priority Queue 更多實際用途 ### 改善方向 - ### 相關題目 -- [題目編號] 題目名稱 - 相似概念 -- [題目編號] 題目名稱 - 進階版本 +- [#1627](https://leetcode.com/problems/graph-connectivity-with-threshold/description/) Graph Connectivity With Threshold +- [#1728](https://leetcode.com/problems/cat-and-mouse-ii/description/) Cat and Mouse II +- [#3440](https://leetcode.com/problems/reschedule-meetings-for-maximum-free-time-ii/description/) Reschedule Meetings for Maximum Free Time II --- -**總結**: 這題的核心概念是...,適合練習...技巧。 +**總結**: +1. 貪心策略,每次都選擇當下收益最大解 +2. 練習使用Priority Queue 快速找到最大收益 -- 2.49.1 From 2c72e5236fc3e459b5d0aabebc0b7fc608f77e9e Mon Sep 17 00:00:00 2001 From: MH Hung Date: Mon, 1 Sep 2025 13:45:31 +0800 Subject: [PATCH 5/6] [#1782] feat(MaxAverageRatio): Add priority queue method --- .../csharp/Program.cs | 65 +++++++++++++++++-- 1 file changed, 61 insertions(+), 4 deletions(-) diff --git a/problems/1792-maximum-average-pass-ratio/csharp/Program.cs b/problems/1792-maximum-average-pass-ratio/csharp/Program.cs index bd54a7b..4376c93 100644 --- a/problems/1792-maximum-average-pass-ratio/csharp/Program.cs +++ b/problems/1792-maximum-average-pass-ratio/csharp/Program.cs @@ -6,7 +6,45 @@ using System; using System.Collections.Generic; using System.Linq; -public class Solution { +public class Solution +{ + public double MaxAverageRatio(int[][] classes, int extraStudents) + { + // use priority queue + var pq = new PriorityQueue(); + + // 初始化:將所有班級加入優先佇列 + for (int i = 0; i < classes.Length; i++) + { + int pass = classes[i][0]; + int total = classes[i][1]; + + double gain = CalculateGain(pass, total); + + pq.Enqueue(new ClassInfo(pass, total), -gain); + } + + for (int i = 0; i < extraStudents; i++) + { + var classInfo = pq.Dequeue(); + + classInfo.Pass++; + classInfo.Total++; + + double newGain = CalculateGain(classInfo.Pass, classInfo.Total); + pq.Enqueue(classInfo, -newGain); + } + + double totalRatio = 0; + while (pq.Count > 0) + { + var classInfo = pq.Dequeue(); + totalRatio += (double)classInfo.Pass / classInfo.Total; + } + + return totalRatio / classes.Length; + } + public double MaxAverageRatioByBruteForce(int[][] classes, int extraStudents) { var n = classes.Length; @@ -54,7 +92,14 @@ public class Solution { return CalculateAvergaeRatio(pass, total); } - private double CalculateAvergaeRatio(int[] pass, int[] total) { + private double CalculateGain(int pass, int total) + { + // 計算提升率 + return (double)(pass + 1) / (total + 1) - (double)pass / total; + } + + private double CalculateAvergaeRatio(int[] pass, int[] total) + { double sum = 0; for (var i = 0; i < pass.Length; i++) { @@ -64,6 +109,18 @@ public class Solution { } } +public class ClassInfo +{ + public int Pass { get; set; } + public int Total { get; set; } + + public ClassInfo(int pass, int total) + { + Pass = pass; + Total = total; + } +} + // 測試程式 public class Program { @@ -98,7 +155,7 @@ public class Program // Actual: Console.WriteLine("Actual: "); Console.WriteLine($"Brute Force: {solution.MaxAverageRatioByBruteForce(classes, extraStudents):F6}"); - //Console.WriteLine($"output: {solution.MaxAverageRatio(classes, extraStudents):F6}"); + Console.WriteLine($"Priority Queue: {solution.MaxAverageRatio(classes, extraStudents):F6}"); } @@ -125,6 +182,6 @@ public class Program // Actual: Console.WriteLine("Actual: "); Console.WriteLine($"Brute Force: {solution.MaxAverageRatioByBruteForce(classes, extraStudents):F6}"); - //Console.WriteLine($"output: {solution.MaxAverageRatio(classes, extraStudents):F6}"); + Console.WriteLine($"Priority Queue: {solution.MaxAverageRatio(classes, extraStudents):F6}"); } } -- 2.49.1 From e3edc716efcb40959116e20b41c5344442b21fb9 Mon Sep 17 00:00:00 2001 From: MH Hung Date: Mon, 1 Sep 2025 14:38:58 +0800 Subject: [PATCH 6/6] [#1782] test(MaxAverageRatio): Add run test bash and cases describe --- .../test/edge_cases.md | 7 +++++++ .../test/run_tests.sh | 21 +++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 problems/1792-maximum-average-pass-ratio/test/edge_cases.md create mode 100755 problems/1792-maximum-average-pass-ratio/test/run_tests.sh diff --git a/problems/1792-maximum-average-pass-ratio/test/edge_cases.md b/problems/1792-maximum-average-pass-ratio/test/edge_cases.md new file mode 100644 index 0000000..312723e --- /dev/null +++ b/problems/1792-maximum-average-pass-ratio/test/edge_cases.md @@ -0,0 +1,7 @@ +# 邊界情況清單 + +## 需要測試的邊界情況 +- [x] one element in classes +- [x] classes Length >= 1000 +- [x] extraStudent = 1 +- [x] extraStudent = 10000 diff --git a/problems/1792-maximum-average-pass-ratio/test/run_tests.sh b/problems/1792-maximum-average-pass-ratio/test/run_tests.sh new file mode 100755 index 0000000..1137923 --- /dev/null +++ b/problems/1792-maximum-average-pass-ratio/test/run_tests.sh @@ -0,0 +1,21 @@ +#!/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 "" +echo "📊 測試完成!" -- 2.49.1