freeCodeCamp/curriculum/challenges/japanese/10-coding-interview-prep/project-euler/problem-96-su-doku.md

297 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

---
id: 5900f3cc1000cf542c50fedf
title: '問題 96: 数独'
challengeType: 1
forumTopicId: 302213
dashedName: problem-96-su-doku
---
# --description--
数独 (英語では*「ナンバープレイス」*) は、人気のパズルの概念に使われる名前です。 その起源は不明ですが、レオンハルト・オイラーが考案した「ラテン方陣」を基にしていることは確かです。これは数独と似ていますが、それよりはるかに難しいパズルです。 しかし数独の目的は、9 × 9 の格子の中にある空白 (または 0) を数字に置き換えて、それぞれの行と列、および 3 × 3 の枠に 1 から 9 の数字を 1 つずつ入れることです。 下図は、パズル開始時の典型的な格子と、その解答の格子です。
<div style="margin: auto; background-color: white; padding: 10px; width: 80%; text-align: center;">
<table border="0" cellpadding="0" cellspacing="0" align="center">
<tbody>
<tr>
<td>
<table cellpadding="5" cellspacing="0" border="1">
<tbody>
<tr>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
0 0 3<br />9 0 0<br />0 0 1
</td>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
0 2 0<br />3 0 5<br />8 0 6
</td>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
6 0 0<br />0 0 1<br />4 0 0
</td>
</tr>
<tr>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
0 0 8<br />7 0 0<br />0 0 6
</td>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
1 0 2<br />0 0 0<br />7 0 8
</td>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
9 0 0<br />0 0 8<br />2 0 0
</td>
</tr>
<tr>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
0 0 2<br />8 0 0<br />0 0 5
</td>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
6 0 9<br />2 0 3<br />0 1 0
</td>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
5 0 0<br />0 0 9<br />3 0 0
</td>
</tr>
</tbody>
</table>
</td>
<td width="50">
<img src="images/spacer.gif" width="50" height="1" alt="" /><br />
</td>
<td>
<table cellpadding="5" cellspacing="0" border="1">
<tbody>
<tr>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
4 8 3<br />9 6 7<br />2 5 1
</td>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
9 2 1<br />3 4 5<br />8 7 6
</td>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
6 5 7<br />8 2 1<br />4 9 3
</td>
</tr>
<tr>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
5 4 8<br />7 2 9<br />1 3 6
</td>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
1 3 2<br />5 6 4<br />7 9 8
</td>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
9 7 6<br />1 3 8<br />2 4 5
</td>
</tr>
<tr>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
3 7 2<br />8 1 4<br />6 9 5
</td>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
6 8 9<br />2 5 3<br />4 1 7
</td>
<td style="font-family:'courier new';font-size:14pt; color: black; padding: 5px; border: 2px solid black;">
5 1 4<br />7 6 9<br />3 8 2
</td>
</tr>
</tbody>
</table>
</td>
</tr>
</tbody>
</table>
</div>
「見当をつけて試す」という手段で選択肢を消去していく必要があるかもしれませんが、うまく作られた数独は解を 1 つだけ持ち、論理によって解くことができます (これについては議論が分かれます)。 探索の複雑さがパズルの難易度を決めます。上の例は、単純で直接的な推測で解けるので、簡単なものと言えます。
`puzzlesArr` 配列にはさまざまな難易度の数独文字列が 50 個含まれており、そのすべてが解を 1 つだけ持ちます。
`puzzlesArr` に含まれる 50 個の数独をすべて解き、それぞれの解の格子の左上隅にある 3 桁の数の和を求めなさい。例えば、上の解答例の左上隅の 3 桁は 483 です。
# --hints--
`suDoku(testPuzzles1)` は数値を返す必要があります。
```js
assert(typeof suDoku(_testPuzzles1) === 'number');
```
`suDoku(testPuzzles1)``1190` を返す必要があります。
```js
assert.strictEqual(suDoku(_testPuzzles1), 1190);
```
`suDoku(testPuzzles2)``24702` を返す必要があります。
```js
assert.strictEqual(suDoku(_testPuzzles2), 24702);
```
# --seed--
## --after-user-code--
```js
const _testPuzzles1 = [
'003020600900305001001806400008102900700000008006708200002609500800203009005010300',
'200080300060070084030500209000105408000000000402706000301007040720040060004010003',
'000000907000420180000705026100904000050000040000507009920108000034059000507000000'
];
const _testPuzzles2 = [
'003020600900305001001806400008102900700000008006708200002609500800203009005010300',
'200080300060070084030500209000105408000000000402706000301007040720040060004010003',
'000000907000420180000705026100904000050000040000507009920108000034059000507000000',
'030050040008010500460000012070502080000603000040109030250000098001020600080060020',
'020810740700003100090002805009040087400208003160030200302700060005600008076051090',
'100920000524010000000000070050008102000000000402700090060000000000030945000071006',
'043080250600000000000001094900004070000608000010200003820500000000000005034090710',
'480006902002008001900370060840010200003704100001060049020085007700900600609200018',
'000900002050123400030000160908000000070000090000000205091000050007439020400007000',
'001900003900700160030005007050000009004302600200000070600100030042007006500006800',
'000125400008400000420800000030000095060902010510000060000003049000007200001298000',
'062340750100005600570000040000094800400000006005830000030000091006400007059083260',
'300000000005009000200504000020000700160000058704310600000890100000067080000005437',
'630000000000500008005674000000020000003401020000000345000007004080300902947100080',
'000020040008035000000070602031046970200000000000501203049000730000000010800004000',
'361025900080960010400000057008000471000603000259000800740000005020018060005470329',
'050807020600010090702540006070020301504000908103080070900076205060090003080103040',
'080005000000003457000070809060400903007010500408007020901020000842300000000100080',
'003502900000040000106000305900251008070408030800763001308000104000020000005104800',
'000000000009805100051907420290401065000000000140508093026709580005103600000000000',
'020030090000907000900208005004806500607000208003102900800605007000309000030020050',
'005000006070009020000500107804150000000803000000092805907006000030400010200000600',
'040000050001943600009000300600050002103000506800020007005000200002436700030000040',
'004000000000030002390700080400009001209801307600200008010008053900040000000000800',
'360020089000361000000000000803000602400603007607000108000000000000418000970030014',
'500400060009000800640020000000001008208000501700500000000090084003000600060003002',
'007256400400000005010030060000508000008060200000107000030070090200000004006312700',
'000000000079050180800000007007306800450708096003502700700000005016030420000000000',
'030000080009000500007509200700105008020090030900402001004207100002000800070000090',
'200170603050000100000006079000040700000801000009050000310400000005000060906037002',
'000000080800701040040020030374000900000030000005000321010060050050802006080000000',
'000000085000210009960080100500800016000000000890006007009070052300054000480000000',
'608070502050608070002000300500090006040302050800050003005000200010704090409060701',
'050010040107000602000905000208030501040070020901080406000401000304000709020060010',
'053000790009753400100000002090080010000907000080030070500000003007641200061000940',
'006080300049070250000405000600317004007000800100826009000702000075040190003090600',
'005080700700204005320000084060105040008000500070803010450000091600508007003010600',
'000900800128006400070800060800430007500000009600079008090004010003600284001007000',
'000080000270000054095000810009806400020403060006905100017000620460000038000090000',
'000602000400050001085010620038206710000000000019407350026040530900020007000809000',
'000900002050123400030000160908000000070000090000000205091000050007439020400007000',
'380000000000400785009020300060090000800302009000040070001070500495006000000000092',
'000158000002060800030000040027030510000000000046080790050000080004070100000325000',
'010500200900001000002008030500030007008000500600080004040100700000700006003004050',
'080000040000469000400000007005904600070608030008502100900000005000781000060000010',
'904200007010000000000706500000800090020904060040002000001607000000000030300005702',
'000700800006000031040002000024070000010030080000060290000800070860000500002006000',
'001007090590080001030000080000005800050060020004100000080000030100020079020700400',
'000003017015009008060000000100007000009000200000500004000000020500600340340200000',
'300200000000107000706030500070009080900020004010800050009040301000702000000008006'
];
```
## --seed-contents--
```js
function suDoku(puzzlesArr) {
return true;
}
// Only change code above this line
const testPuzzles1 = [
'003020600900305001001806400008102900700000008006708200002609500800203009005010300',
'200080300060070084030500209000105408000000000402706000301007040720040060004010003',
'000000907000420180000705026100904000050000040000507009920108000034059000507000000'
];
suDoku(testPuzzles1);
```
# --solutions--
```js
function suDoku(puzzlesArr) {
function solve(puzzle) {
for (let row = 0; row < 9; row++) {
for (let col = 0; col < 9; col++) {
if (puzzle[row][col] > 0) {
continue;
}
const allowedNumbers = getAllowedNumbers(puzzle, row, col);
for (let number = 1; number <= 9; number++) {
if (allowedNumbers[number]) {
puzzle[row][col] = number;
if (solve(puzzle)) {
return true;
}
}
}
puzzle[row][col] = 0;
return false;
}
}
return true;
}
function getAllowedNumbers(puzzle, row, col) {
const allowed = new Array(10).fill(true);
allowed[0] = false;
for (let i = 0; i < 9; i++) {
const numberInSameRow = puzzle[row][i];
if (numberInSameRow > 0) {
allowed[numberInSameRow] = false;
}
const numberInSameCol = puzzle[i][col];
if (numberInSameCol > 0) {
allowed[numberInSameCol] = false;
}
}
const rowOfSubGrid = Math.floor(row / 3) * 3;
const colOfSubGrid = Math.floor(col / 3) * 3;
for (let rowInSubGrid = 0; rowInSubGrid < 3; rowInSubGrid++) {
for (let colInSubGrid = 0; colInSubGrid < 3; colInSubGrid++) {
const numberInSameSubGrid =
puzzle[rowOfSubGrid + rowInSubGrid][colOfSubGrid + colInSubGrid];
if (numberInSameSubGrid > 0) {
allowed[numberInSameSubGrid] = false;
}
}
}
return allowed;
}
function parsePuzzle(string) {
const puzzle = [];
for (let row = 0; row < 9; row++) {
puzzle.push(
string
.slice(row * 9, 9 + row * 9)
.split('')
.map(x => parseInt(x, 10))
);
}
return puzzle;
}
let sum = 0;
for (let i = 0; i < puzzlesArr.length; i++) {
const puzzle = parsePuzzle(puzzlesArr[i]);
if (solve(puzzle)) {
sum += 100 * puzzle[0][0] + 10 * puzzle[0][1] + puzzle[0][2];
}
}
return sum;
}
```