|
1 | 1 | /* |
2 | 2 | * Task: Verify whether a square matrix is a magic square. |
3 | 3 | * |
4 | | - * MAGIC SQUARE VERIFICATION |
| 4 | + * A magic square is an n x n matrix where: |
| 5 | + * - Every row sum is the same |
| 6 | + * - Every column sum is the same |
| 7 | + * - Main diagonal sum is the same |
| 8 | + * - Anti-diagonal sum is the same |
5 | 9 | * |
6 | | - * Check if a given square matrix is a magic square. A magic square is defined |
7 | | - * as an n x n matrix in which the sums of each row, each column, the main |
8 | | - * diagonal, and the anti-diagonal are all equal. |
| 10 | + * Example (3x3): |
| 11 | + * [ 8, 1, 6 ] |
| 12 | + * [ 3, 5, 7 ] |
| 13 | + * [ 4, 9, 2 ] |
| 14 | + * All sums = 15 -> true |
9 | 15 | * |
10 | | - * ASCII Illustration: |
11 | | - * |
12 | | - * [ 8, 1, 6 ] |
13 | | - * [ 3, 5, 7 ] |
14 | | - * [ 4, 9, 2 ] |
15 | | - * |
16 | | - * All rows, columns, and diagonals sum to 15. |
17 | | - * |
18 | | - * Example: |
19 | | - * Input: |
20 | | - * magicSquare = [ [8, 1, 6], |
21 | | - * [3, 5, 7], |
22 | | - * [4, 9, 2] ] |
23 | | - * |
24 | | - * Output: |
25 | | - * true |
26 | | - * |
27 | | - * Explanation: |
28 | | - * Each row, column, and diagonal sums to 15, hence the matrix is a magic |
29 | | - * square. |
30 | 16 | */ |
31 | 17 |
|
32 | 18 | #include <iostream> |
33 | | -#include <numeric> |
| 19 | +#include <stdexcept> |
34 | 20 | #include <string> |
35 | 21 | #include <vector> |
36 | 22 |
|
37 | | -// ----------------------- Simple (Brute-force) Solution ----------------------- |
38 | | -bool isMagicSquareSimple(const std::vector<std::vector<int>> &matrix) { |
39 | | - int n = matrix.size(); |
40 | | - if (n == 0) |
41 | | - return false; |
42 | | - // Check if matrix is square. |
43 | | - for (const auto &row : matrix) { |
44 | | - if (row.size() != static_cast<size_t>(n)) |
45 | | - return false; |
| 23 | +// ----------------------- 1) Simple (Brute-force) Solution -------------------- |
| 24 | +// |
| 25 | +// Idea: |
| 26 | +// - Compute target sum from first row. |
| 27 | +// - Check all row sums, all column sums, then both diagonals. |
| 28 | +// |
| 29 | +// Time Complexity: |
| 30 | +// - Row checks: O(n^2) |
| 31 | +// - Column checks: O(n^2) |
| 32 | +// - Diagonals: O(n) |
| 33 | +// => Total: O(n^2) |
| 34 | +// |
| 35 | +// Space Complexity: |
| 36 | +// - O(1) extra space. |
| 37 | +// |
| 38 | +bool isMagicSquareSimple(const std::vector<std::vector<int>>& matrix) { |
| 39 | + const int n = static_cast<int>(matrix.size()); |
| 40 | + if (n == 0) return false; |
| 41 | + |
| 42 | + // Validate square matrix: O(n) |
| 43 | + for (const auto& row : matrix) { |
| 44 | + if (static_cast<int>(row.size()) != n) return false; |
46 | 45 | } |
47 | 46 |
|
48 | | - // Compute the target sum from the first row. |
| 47 | + // Target sum = first row: O(n) |
49 | 48 | int target = 0; |
50 | | - for (int num : matrix[0]) |
51 | | - target += num; |
52 | | - |
53 | | - // Check row sums. |
54 | | - for (const auto &row : matrix) { |
55 | | - int sum = 0; |
56 | | - for (int num : row) |
57 | | - sum += num; |
58 | | - if (sum != target) |
59 | | - return false; |
| 49 | + for (int v : matrix[0]) target += v; |
| 50 | + |
| 51 | + // Check rows: O(n^2) |
| 52 | + for (int i = 0; i < n; ++i) { |
| 53 | + int rowSum = 0; |
| 54 | + for (int j = 0; j < n; ++j) rowSum += matrix[i][j]; |
| 55 | + if (rowSum != target) return false; |
60 | 56 | } |
61 | 57 |
|
62 | | - // Check column sums. |
| 58 | + // Check columns: O(n^2) |
63 | 59 | for (int j = 0; j < n; ++j) { |
64 | | - int sum = 0; |
65 | | - for (int i = 0; i < n; ++i) |
66 | | - sum += matrix[i][j]; |
67 | | - if (sum != target) |
68 | | - return false; |
| 60 | + int colSum = 0; |
| 61 | + for (int i = 0; i < n; ++i) colSum += matrix[i][j]; |
| 62 | + if (colSum != target) return false; |
69 | 63 | } |
70 | 64 |
|
71 | | - // Check main diagonal. |
| 65 | + // Check main diagonal: O(n) |
72 | 66 | int diag1 = 0; |
73 | | - for (int i = 0; i < n; ++i) |
74 | | - diag1 += matrix[i][i]; |
75 | | - if (diag1 != target) |
76 | | - return false; |
| 67 | + for (int i = 0; i < n; ++i) diag1 += matrix[i][i]; |
| 68 | + if (diag1 != target) return false; |
77 | 69 |
|
78 | | - // Check anti-diagonal. |
| 70 | + // Check anti-diagonal: O(n) |
79 | 71 | int diag2 = 0; |
80 | | - for (int i = 0; i < n; ++i) |
81 | | - diag2 += matrix[i][n - 1 - i]; |
82 | | - if (diag2 != target) |
83 | | - return false; |
| 72 | + for (int i = 0; i < n; ++i) diag2 += matrix[i][n - 1 - i]; |
| 73 | + if (diag2 != target) return false; |
84 | 74 |
|
85 | 75 | return true; |
86 | 76 | } |
87 | 77 |
|
88 | | -// ----------------------- Optimal (Single-pass) Solution |
89 | | -// ----------------------- |
90 | | -bool isMagicSquareOptimal(const std::vector<std::vector<int>> &matrix) { |
91 | | - int n = matrix.size(); |
92 | | - if (n == 0) |
93 | | - return false; |
94 | | - for (const auto &row : matrix) |
95 | | - if (row.size() != static_cast<size_t>(n)) |
96 | | - return false; |
97 | | - |
98 | | - // Calculate target sum from the first row. |
| 78 | +// ----------------------- 2) Optimal (Single-pass) Solution ------------------- |
| 79 | +// |
| 80 | +// Idea: |
| 81 | +// - Compute target from first row. |
| 82 | +// - In ONE traversal: |
| 83 | +// * compute each row sum and compare immediately |
| 84 | +// * accumulate column sums |
| 85 | +// * accumulate both diagonals |
| 86 | +// - After traversal, verify diagonals + column sums. |
| 87 | +// |
| 88 | +// Time Complexity: |
| 89 | +// - Single traversal of all cells: O(n^2) |
| 90 | +// - Final column verification: O(n) |
| 91 | +// => Total: O(n^2) |
| 92 | +// |
| 93 | +// Space Complexity: |
| 94 | +// - O(n) for column sums. |
| 95 | +// |
| 96 | +bool isMagicSquareOptimal(const std::vector<std::vector<int>>& matrix) { |
| 97 | + const int n = static_cast<int>(matrix.size()); |
| 98 | + if (n == 0) return false; |
| 99 | + |
| 100 | + // Validate square matrix: O(n) |
| 101 | + for (const auto& row : matrix) { |
| 102 | + if (static_cast<int>(row.size()) != n) return false; |
| 103 | + } |
| 104 | + |
| 105 | + // Target sum = first row: O(n) |
99 | 106 | int target = 0; |
100 | | - for (int j = 0; j < n; ++j) |
101 | | - target += matrix[0][j]; |
| 107 | + for (int v : matrix[0]) target += v; |
102 | 108 |
|
| 109 | + std::vector<int> colSum(n, 0); // O(n) space |
103 | 110 | int diag1 = 0, diag2 = 0; |
104 | | - // Initialize column sums. |
105 | | - std::vector<int> colSum(n, 0); |
106 | 111 |
|
| 112 | + // Single pass over matrix: O(n^2) |
107 | 113 | for (int i = 0; i < n; ++i) { |
108 | 114 | int rowSum = 0; |
109 | 115 | for (int j = 0; j < n; ++j) { |
110 | | - rowSum += matrix[i][j]; |
111 | | - colSum[j] += matrix[i][j]; |
| 116 | + const int val = matrix[i][j]; |
| 117 | + rowSum += val; // row sum |
| 118 | + colSum[j] += val; // column sums |
112 | 119 | } |
113 | | - if (rowSum != target) |
114 | | - return false; |
115 | | - diag1 += matrix[i][i]; |
116 | | - diag2 += matrix[i][n - 1 - i]; |
117 | | - } |
| 120 | + if (rowSum != target) return false; // early exit |
118 | 121 |
|
119 | | - if (diag1 != target || diag2 != target) |
120 | | - return false; |
121 | | - |
122 | | - for (int sum : colSum) |
123 | | - if (sum != target) |
124 | | - return false; |
125 | | - |
126 | | - return true; |
127 | | -} |
128 | | - |
129 | | -// ----------------------- Alternative (STL-based) Solution |
130 | | -// ----------------------- |
131 | | -bool isMagicSquareAlternative(const std::vector<std::vector<int>> &matrix) { |
132 | | - int n = matrix.size(); |
133 | | - if (n == 0) |
134 | | - return false; |
135 | | - for (const auto &row : matrix) |
136 | | - if (row.size() != static_cast<size_t>(n)) |
137 | | - return false; |
138 | | - |
139 | | - // Compute target sum using std::accumulate on first row. |
140 | | - int target = std::accumulate(matrix[0].begin(), matrix[0].end(), 0); |
141 | | - |
142 | | - // Check rows using std::accumulate. |
143 | | - for (const auto &row : matrix) { |
144 | | - if (std::accumulate(row.begin(), row.end(), 0) != target) |
145 | | - return false; |
| 122 | + diag1 += matrix[i][i]; // main diagonal |
| 123 | + diag2 += matrix[i][n - 1 - i]; // anti-diagonal |
146 | 124 | } |
147 | 125 |
|
148 | | - // Check columns. |
| 126 | + // Diagonals: O(1) check |
| 127 | + if (diag1 != target || diag2 != target) return false; |
| 128 | + |
| 129 | + // Columns: O(n) |
149 | 130 | for (int j = 0; j < n; ++j) { |
150 | | - int colSum = 0; |
151 | | - for (int i = 0; i < n; ++i) |
152 | | - colSum += matrix[i][j]; |
153 | | - if (colSum != target) |
154 | | - return false; |
| 131 | + if (colSum[j] != target) return false; |
155 | 132 | } |
156 | 133 |
|
157 | | - // Check main diagonal. |
158 | | - int diag1 = 0; |
159 | | - for (int i = 0; i < n; ++i) |
160 | | - diag1 += matrix[i][i]; |
161 | | - if (diag1 != target) |
162 | | - return false; |
163 | | - |
164 | | - // Check anti-diagonal. |
165 | | - int diag2 = 0; |
166 | | - for (int i = 0; i < n; ++i) |
167 | | - diag2 += matrix[i][n - 1 - i]; |
168 | | - if (diag2 != target) |
169 | | - return false; |
170 | | - |
171 | 134 | return true; |
172 | 135 | } |
173 | 136 |
|
174 | | -namespace { |
| 137 | +// ------------------------------ Tests (2 examples) --------------------------- |
| 138 | +// |
| 139 | +// Time Complexity of tests is irrelevant; tiny fixed-size inputs. |
| 140 | +// |
175 | 141 | struct TestRunner { |
176 | 142 | int total = 0; |
177 | 143 | int failed = 0; |
178 | 144 |
|
179 | | - void expectEqual(bool got, bool expected, const std::string &label) { |
| 145 | + void expectEqual(bool got, bool expected, const std::string& label) { |
180 | 146 | ++total; |
181 | 147 | if (got == expected) { |
182 | 148 | std::cout << "[PASS] " << label << "\n"; |
183 | | - return; |
| 149 | + } else { |
| 150 | + ++failed; |
| 151 | + std::cout << "[FAIL] " << label << " expected=" << std::boolalpha |
| 152 | + << expected << " got=" << got << "\n"; |
184 | 153 | } |
185 | | - ++failed; |
186 | | - std::cout << "[FAIL] " << label << " expected=" << std::boolalpha |
187 | | - << expected << " got=" << got << "\n"; |
188 | 154 | } |
189 | 155 |
|
190 | 156 | void summary() const { |
191 | | - std::cout << "Tests: " << total - failed << " passed, " << failed |
| 157 | + std::cout << "Tests: " << (total - failed) << " passed, " << failed |
192 | 158 | << " failed, " << total << " total\n"; |
193 | 159 | } |
194 | 160 | }; |
195 | | -} // namespace |
196 | | - |
197 | | -// ----------------------- Test cases for correctness ----------------------- |
198 | | -void test() { |
199 | | - std::vector<std::vector<int>> magicSquare = {{8, 1, 6}, {3, 5, 7}, {4, 9, 2}}; |
200 | | - |
201 | | - std::vector<std::vector<int>> nonMagicSquare = { |
202 | | - {5, 3, 4}, {1, 5, 8}, {6, 4, 2}}; |
203 | | - TestRunner runner; |
204 | | - |
205 | | - // Test Simple Solution |
206 | | - runner.expectEqual(isMagicSquareSimple(magicSquare), true, "simple magic"); |
207 | | - runner.expectEqual(isMagicSquareSimple(nonMagicSquare), false, |
208 | | - "simple non-magic"); |
209 | | - |
210 | | - // Test Optimal Solution |
211 | | - runner.expectEqual(isMagicSquareOptimal(magicSquare), true, "optimal magic"); |
212 | | - runner.expectEqual(isMagicSquareOptimal(nonMagicSquare), false, |
213 | | - "optimal non-magic"); |
214 | | - |
215 | | - // Test Alternative Solution |
216 | | - runner.expectEqual(isMagicSquareAlternative(magicSquare), true, |
217 | | - "alternative magic"); |
218 | | - runner.expectEqual(isMagicSquareAlternative(nonMagicSquare), false, |
219 | | - "alternative non-magic"); |
220 | | - runner.summary(); |
221 | | -} |
222 | 161 |
|
223 | 162 | int main() { |
224 | | - test(); |
225 | | - return 0; |
| 163 | + // Example 1: Magic square -> true |
| 164 | + std::vector<std::vector<int>> magic = { |
| 165 | + {8, 1, 6}, |
| 166 | + {3, 5, 7}, |
| 167 | + {4, 9, 2}, |
| 168 | + }; |
| 169 | + |
| 170 | + // Example 2: Not a magic square -> false |
| 171 | + std::vector<std::vector<int>> notMagic = { |
| 172 | + {5, 3, 4}, |
| 173 | + {1, 5, 8}, |
| 174 | + {6, 4, 2}, |
| 175 | + }; |
| 176 | + |
| 177 | + TestRunner t; |
| 178 | + |
| 179 | + // Simple |
| 180 | + t.expectEqual(isMagicSquareSimple(magic), true, "simple: magic"); |
| 181 | + t.expectEqual(isMagicSquareSimple(notMagic), false, "simple: not magic"); |
| 182 | + |
| 183 | + // Optimal |
| 184 | + t.expectEqual(isMagicSquareOptimal(magic), true, "optimal: magic"); |
| 185 | + t.expectEqual(isMagicSquareOptimal(notMagic), false, "optimal: not magic"); |
| 186 | + |
| 187 | + t.summary(); |
| 188 | + return (t.failed == 0) ? 0 : 1; |
226 | 189 | } |
0 commit comments