以下の問題の記述形式です。
基本情報技術者試験(科目B)の「擬似言語」の形式に基づいた、初学者向けの練習問題です。
上記資料にある記述ルール(配列の添字は1から始まるなど)に準拠し、それぞれの解答、解説、そしてJavaで書いた場合のコードを併記します。
解答解説はアコーディオン形式でクリックにより展開できます。
第1問:変数の演算と代入
【問題】
次のプログラムを実行したとき、出力される ans の値はいくつか。
整数型: a ← 5
整数型: b ← 3
整数型: ans ← a × b + a
ans を出力
【解答】
20
【解説】
基本的な変数の代入と演算の問題です。
aに 5、bに 3 が代入されます。a × bは5 × 3 = 15です。- それに
a(つまり 5) を足すので、15 + 5 = 20となります。 - 結果として
ansに 20 が代入され、出力されます。
【Javaコード】
public class Main {
public static void main(String[] args) {
int a = 5;
int b = 3;
int ans = a * b + a;
System.out.println(ans);
}
}
第2問:条件分岐(if文)
【問題】
次のプログラムを実行したとき、出力される文字列は何か。
整数型: score ← 75
文字列型: result ← "不合格"
if (score ≥ 80)
result ← "優"
elseif (score ≥ 60)
result ← "良"
else
result ← "不可"
endif
result を出力【解答】
良
【解説】
条件分岐(if-elseif-else)の流れを追う問題です 1。
- 最初の条件
score ≥ 80(75は80以上か?)は False なので、次のelseifに進みます。 - 次の条件
score ≥ 60(75は60以上か?)は True です。 - 対応する処理
result ← "良"が実行されます。 - 条件が成立したため、以降の
elseは無視され、endifに抜けます。
【Javaコード】
public class Main {
public static void main(String[] args) {
int score = 75;
String result = "不合格";
if (score >= 80) {
result = "優";
} else if (score >= 60) {
result = "良";
} else {
result = "不可";
}
System.out.println(result);
}
}
第3問:繰り返し処理(while文)
【問題】
次のプログラムを実行したとき、出力される count の値はいくつか。
整数型: i ← 1
整数型: count ← 0
while (i ≤ 10)
count ← count + i
i ← i + 3
endwhile
count を出力【解答】
22
【解説】
while ループによる繰り返し加算です 2。
- 1回目:
i=1 (条件:真)。count= 0+1 = 1。iは 1+3 = 4 になります。 - 2回目:
i=4 (条件:真)。count= 1+4 = 5。iは 4+3 = 7 になります。 - 3回目:
i=7 (条件:真)。count= 5+7 = 12。iは 7+3 = 10 になります。 - 4回目:
i=10 (条件:真)。count= 12+10 = 22。iは 10+3 = 13 になります。 - 5回目:
i=13 (条件:偽)。ループを終了します。
【Javaコード】
public class Main {
public static void main(String[] args) {
int i = 1;
int count = 0;
while (i <= 10) {
count = count + i;
i = i + 3;
}
System.out.println(count);
}
}
第4問:配列とfor文(重要:添字の違い)
【問題】
次のプログラムを実行したとき、出力される total の値はいくつか。
※試験の仕様に基づき、配列の要素番号は1から始まるものとする。
整数型の配列: nums ← {10, 20, 30, 40, 50}
整数型: total ← 0
整数型: k
for (k を 1 から 3 まで 1 ずつ増やす)
total ← total + nums[k]
endfor
total を出力【解答】
60
【解説】
配列の先頭から3つの要素を足し合わせる処理です。
k=1 のとき、nums[1]は 10。total= 0 + 10 = 10。k=2 のとき、nums[2]は 20。total= 10 + 20 = 30。- k=3 のとき、nums[3] は 30。total = 30 + 30 = 60。ループ終了。
注意点:
科目Bの擬似言語では配列の添字(インデックス)が 1 から始まりますが、Javaでは 0 から始まります。Javaコードに変換する際は添字をずらす必要があります。
【Javaコード】
public class Main {
public static void main(String[] args) {
int[] nums = {10, 20, 30, 40, 50};
int total = 0;
// Javaの配列は0番目から始まるため、
// 擬似言語の「1から3まで」は「0から2まで」に対応します。
for (int k = 0; k < 3; k++) {
total = total + nums[k];
}
System.out.println(total);
}
}
第5問:関数の呼び出しと論理演算
【問題】
次のプログラム中の関数 checkValue(15) を呼び出したとき、戻り値として返される文字列は何か。
※演算子 mod は剰余(割り算の余り)を表す 4。
○文字列型: checkValue(整数型: n)
if ((n mod 3 = 0) and (n mod 5 = 0))
return "A"
elseif (n mod 3 = 0)
return "B"
else
return "C"
endif【解答】
"A"
【解説】
FizzBuzz問題のような論理演算です。引数 n は 15 です。
- 最初の条件
(n mod 3 = 0) and (n mod 5 = 0)を評価します。 15 mod 3は 0(割り切れる)。15 mod 5は 0(割り切れる)。- 両方とも成立(True)しているため、
and条件全体が真となり、return "A"が実行されます。
【Javaコード】
public class Main {
public static void main(String[] args) {
System.out.println(checkValue(15));
}
public static String checkValue(int n) {
// Javaでは「mod」の代わりに「%」、「and」の代わりに「&&」を使います
if ((n % 3 == 0) && (n % 5 == 0)) {
return "A";
} else if (n % 3 == 0) {
return "B";
} else {
return "C";
}
}
}
第6問:2次元配列のアクセス
【問題】
次のプログラムを実行したとき、出力される sum の値はいくつか。
※{{...}, {...}} は2次元配列を表し、mat[行][列] でアクセスする 2。
整数型の配列: mat ← {{1, 2, 3},
{4, 5, 6},
{7, 8, 9}}
整数型: sum ← 0
整数型: i
for (i を 1 から 3 まで 1 ずつ増やす)
sum ← sum + mat[i][i]
endfor
sum を出力
【解答】
15
【解説】
正方形の行列(3x3)の対角線上の要素を足し合わせる処理です。
i=1:mat[1][1]は 1。sum= 0 + 1 = 1。i=2:mat[2][2]は 5。sum= 1 + 5 = 6。- i=3: mat[3][3] は 9。sum = 6 + 9 = 15。結果、15が出力されます。
【Javaコード】
public class Main {
public static void main(String[] args) {
int[][] mat = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
int sum = 0;
// Javaは0始まりなので、mat[i][i]でアクセスするために0から2まで回す
for (int i = 0; i < 3; i++) {
sum = sum + mat[i][i];
}
System.out.println(sum);
}
}
第7問:配列内の最大値探索
【問題】
次のプログラムを実行したとき、出力される maxVal の値はいくつか。
整数型の配列: scores ← {10, 50, 30, 20, 40}
整数型: maxVal ← scores[1]
整数型: i
for (i を 2 から scoresの要素数 まで 1 ずつ増やす)
if (scores[i] > maxVal)
maxVal ← scores[i]
endif
endfor
maxVal を出力
【解答】
50
【解説】
配列の中から最大値を見つけるアルゴリズムです。
50
【解説】
配列の中から最大値を見つけるアルゴリズムです。
- 最初に
maxValにscores[1](つまり 10)を仮置きします。 i=2:scores[2](50) とmaxVal(10) を比較。50 > 10 なのでmaxValは 50 に更新。i=3:scores[3](30) とmaxVal(50) を比較。更新なし。- 以降、20, 40 と比較しても 50 を超えないため更新なし。
- 最終的に 50 が出力されます。
【Javaコード】
public class Main {
public static void main(String[] args) {
int[] scores = {10, 50, 30, 20, 40};
int maxVal = scores[0]; // Javaは0番目が先頭
// 2番目の要素(index 1)から最後まで比較
for (int i = 1; i < scores.length; i++) {
if (scores[i] > maxVal) {
maxVal = scores[i];
}
}
System.out.println(maxVal);
}
}
第8問:再帰関数(再帰呼び出し)
【問題】
次のプログラム中の関数 sigma(3) を呼び出したとき、戻り値はいくつか。
○整数型: sigma(整数型: n)
if (n = 0)
return 0
endif
return n + sigma(n - 1)
【解答】
6
【解説】
自分自身を呼び出す「再帰関数」の問題です 3。
sigma(n) は 1からnまでの和を求める関数として動作します。
sigma(3)呼び出し: 返り値は3 + sigma(2)sigma(2)呼び出し: 返り値は2 + sigma(1)sigma(1)呼び出し: 返り値は1 + sigma(0)- sigma(0) 呼び出し: 条件 n=0 に合致し、0 を返す。これらを積み上げると、3 + (2 + (1 + 0)) = 6 となります。
【Javaコード】
public class Main {
public static void main(String[] args) {
System.out.println(sigma(3));
}
public static int sigma(int n) {
if (n == 0) {
return 0;
}
return n + sigma(n - 1);
}
}
第9問:ネストされたループ(二重ループ)
【問題】
次のプログラムを実行したとき、出力される count の値はいくつか。
整数型: count ← 0
整数型: i, j
for (i を 1 から 3 まで 1 ずつ増やす)
for (j を 1 から i まで 1 ずつ増やす)
count ← count + 1
endfor
endfor
count を出力
【解答】
6
【解説】
内側のループの回数が、外側の変数 i に依存して変化します。
- i = 1 のとき: 内側のループ
jは 1 から 1 まで(1回)。count= 1。 - i = 2 のとき: 内側のループ
jは 1 から 2 まで(2回)。count= 1 + 2 = 3。 - i = 3 のとき: 内側のループ j は 1 から 3 まで(3回)。count = 3 + 3 = 6。合計でループが6回回るため、答えは6です。
【Javaコード】
public class Main {
public static void main(String[] args) {
int count = 0;
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= i; j++) {
count = count + 1;
}
}
System.out.println(count);
}
}
第10問:配列の要素の入れ替え
【問題】
次のプログラムを実行したとき、出力される配列 data の内容はどれか。
※ div は整数の割り算の商(小数点以下切り捨て)を表すものとする。
整数型の配列: data ← {1, 2, 3, 4, 5}
整数型: n ← dataの要素数
整数型: i, temp
for (i を 1 から (n ÷ 2) の商 まで 1 ずつ増やす)
/* 値の入れ替え */
temp ← data[i]
data[i] ← data[n - i + 1]
data[n - i + 1] ← temp
endfor
data の全要素を出力
【解答】
{5, 4, 3, 2, 1}
【解説】
配列の順序を逆転(リバース)させるアルゴリズムです。
nは 5 なので、ループは1から2(5÷2の商) まで実行されます。
- i = 1 のとき:
data[1](1) とdata[5 - 1 + 1]つまりdata[5](5) を交換します。- 配列は
{5, 2, 3, 4, 1}になります。
- 配列は
- i = 2 のとき:
data[2](2) とdata[5 - 2 + 1]つまりdata[4](4) を交換します。- 配列は
{5, 4, 3, 2, 1}になります。
- 配列は
- ループ終了。真ん中の
3は触られませんが、結果として配列全体が逆順になります。
【Javaコード】
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] data = {1, 2, 3, 4, 5};
int n = data.length;
int temp;
// Javaの添字は0〜(n-1)。
// 擬似言語の「iを1から n/2」は、Javaでは「iを0から n/2 - 1」まで等の調整が必要ですが、
// ここではロジックを分かりやすくJava風に書き直します。
// 左端(i)と右端(n-1-i)を交換します。
for (int i = 0; i < n / 2; i++) {
temp = data[i];
data[i] = data[n - 1 - i];
data[n - 1 - i] = temp;
}
System.out.println(Arrays.toString(data));
}
}第11問:2分探索(バイナリサーチ)
【問題】
次のプログラムは、昇順に整列された配列 data から、引数 target と等しい値を持つ要素を探し、その添字を返す関数 binarySearch である。
binarySearch({10, 20, 30, 40, 50, 60, 70}, 20) を呼び出したとき、変数 middle に代入される値の変化として正しいものはどれか。
※ ÷ は小数点以下を切り捨てる除算とする。
○整数型: binarySearch(整数型の配列: data, 整数型: target)
整数型: low ← 1
整数型: high ← dataの要素数
整数型: middle
while (low ≤ high)
middle ← (low + high) ÷ 2
if (data[middle] = target)
return middle
elseif (data[middle] < target)
low ← middle + 1
else
high ← middle - 1
endif
endwhile
return -1
【解答】
4 → 2
【解説】
探索範囲を半分に絞り込んでいく過程をトレースします。
- 初期状態:
low = 1,high = 7
- 1回目のループ:
middle←(1 + 7) ÷ 2= 4data[4]は40。40 > 20(target) なので、else節へ。high←4 - 1= 3。
- 2回目のループ:
low = 1,high = 3middle←(1 + 3) ÷ 2= 2data[2]は20。- 20 = 20 (target) なので、return middle (2) で終了。よって、middle の値は 4 → 2 と変化します。
【Javaコード】
public class Main {
public static void main(String[] args) {
int[] data = {10, 20, 30, 40, 50, 60, 70};
System.out.println(binarySearch(data, 20));
}
public static int binarySearch(int[] data, int target) {
int low = 0; // Javaは0始まり
int high = data.length - 1;
int middle;
while (low <= high) {
middle = (low + high) / 2;
System.out.println("middle: " + middle); // 変化を確認
if (data[middle] == target) {
return middle;
} else if (data[middle] < target) {
low = middle + 1;
} else {
high = middle - 1;
}
}
return -1;
}
}
第12問:文字列の圧縮(ランレングス符号化の基本)
【問題】
次のプログラムを実行したとき、出力される文字列は何か。
※演算子 + は文字列の連結を表す。
文字列型: s ← "AAABCCCC"
文字列型: res ← ""
整数型: count ← 1
整数型: i
for (i を 1 から sの文字数 - 1 まで 1 ずつ増やす)
if (s の i 文字目 = s の (i + 1) 文字目)
count ← count + 1
else
res ← res + s の i 文字目 + countを文字列にしたもの
count ← 1
endif
endfor
res ← res + s の 末尾の文字 + countを文字列にしたもの
res を出力【解答】
"A3B1C4"
【解説】
連続する文字を「文字+個数」の形式に変換するアルゴリズムです。
i=1('A'),i+1('A') → 一致。count=2。i=2('A'),i+1('A') → 一致。count=3。i=3('A'),i+1('B') → 不一致。resに "A3" を追加。count=1 にリセット。i=4('B'),i+1('C') → 不一致。resに "B1" を追加(現在は "A3B1")。count=1 にリセット。i=5('C'),i+1('C') → 一致。count=2。i=6('C'),i+1('C') → 一致。count=3。i=7('C'),i+1('C') → 一致。count=4。- ループ終了後、最後の文字とカウントを追加。res に "C4" を追加。結果は "A3B1C4" となります。
【Javaコード】
public class Main {
public static void main(String[] args) {
String s = "AAABCCCC";
String res = "";
int count = 1;
for (int i = 0; i < s.length() - 1; i++) {
if (s.charAt(i) == s.charAt(i + 1)) {
count++;
} else {
res = res + s.charAt(i) + count;
count = 1;
}
}
// 最後の部分を追加
res = res + s.charAt(s.length() - 1) + count;
System.out.println(res);
}
}
第13問:再帰関数(ユークリッドの互除法)
【問題】
次のプログラム中の関数 calc(24, 9) を呼び出したとき、戻り値はいくつか。
※ mod は剰余を表す。
○整数型: calc(整数型: a, 整数型: b)
if (b = 0)
return a
endif
return calc(b, a mod b)【解答】
3
【解説】
これは最大公約数(GCD)を求める「ユークリッドの互除法」の実装です。
calc(24, 9)呼び出し。b(9) は 0 でない。24 mod 9= 6calc(9, 6)を呼び出す。
calc(9, 6)呼び出し。b(6) は 0 でない。9 mod 6= 3calc(6, 3)を呼び出す。
calc(6, 3)呼び出し。b(3) は 0 でない。6 mod 3= 0calc(3, 0)を呼び出す。
calc(3, 0)呼び出し。bは 0 である。a(つまり 3) を返す。
【Javaコード】
public class Main {
public static void main(String[] args) {
System.out.println(calc(24, 9));
}
public static int calc(int a, int b) {
if (b == 0) {
return a;
}
return calc(b, a % b);
}
}
第14問:配列の操作(シフト処理)
【問題】
次のプログラムを実行したとき、出力される配列 arr の内容はどれか。
整数型の配列: arr ← {1, 2, 3, 4, 5}
整数型: temp
整数型: i
temp ← arr[1]
for (i を 1 から arrの要素数 - 1 まで 1 ずつ増やす)
arr[i] ← arr[i + 1]
endfor
arr[arrの要素数] ← temp
arr を出力【解答】
{2, 3, 4, 5, 1}
【解説】
配列の要素を「左に1つ回転シフト」させる処理です。
- 先頭の
arr[1](値は1) をtempに退避します。 - ループ処理で、
i=1から4まで実行します。arr[1] ← arr[2](2が入る)arr[2] ← arr[3](3が入る)arr[3] ← arr[4](4が入る)arr[4] ← arr[5](5が入る)
- 最後に、末尾 arr[5] に退避しておいた temp (1) を入れます。結果、{2, 3, 4, 5, 1} となります。
【Javaコード】
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] arr = {1, 2, 3, 4, 5};
int temp = arr[0]; // 先頭を退避
for (int i = 0; i < arr.length - 1; i++) {
arr[i] = arr[i + 1];
}
arr[arr.length - 1] = temp; // 末尾に代入
System.out.println(Arrays.toString(arr));
}
}
第15問:スタック的な処理(LIFO)
【問題】
次のプログラムを実行したとき、出力される値はいくつか。
※関数 push は配列の末尾に要素を追加し、関数 pop は配列の末尾の要素を取り出して削除し、その値を返すものとする。
整数型の配列: stack ← {}
push(stack, 10)
push(stack, 20)
整数型: a ← pop(stack)
push(stack, 30)
push(stack, 40)
整数型: b ← pop(stack)
整数型: c ← pop(stack)
(a + b - c) を出力【解答】
30
【解説】
スタック(後入れ先出し:LIFO)の動作を確認する問題です。
stack:{10}stack:{10, 20}a ← pop(stack): 末尾の 20 を取り出す。a = 20。stack:{10}stack:{10, 30}stack:{10, 30, 40}b ← pop(stack): 末尾の 40 を取り出す。b = 40。stack:{10, 30}c ← pop(stack): 末尾の 30 を取り出す。c = 30。stack:{10}- 計算:
a + b - c=20 + 40 - 30= 30
【Javaコード】
Javaでは java.util.Stack クラスなどを使用しますが、ここでは ArrayList を使って動作を模倣します。
import java.util.ArrayList;
import java.util.List;
public class Main {
public static void main(String[] args) {
List<Integer> stack = new ArrayList<>();
stack.add(10); // push
stack.add(20); // push
int a = stack.remove(stack.size() - 1); // pop
stack.add(30); // push
stack.add(40); // push
int b = stack.remove(stack.size() - 1); // pop
int c = stack.remove(stack.size() - 1); // pop
System.out.println(a + b - c);
}
}第16問:単方向リストの操作
【問題】
次のプログラムは、単方向リストの先頭から2番目の要素を削除する処理である。
リストの構造は [Head] -> [A] -> [B] -> [C] となっており、Head.next は要素Aを指している。
プログラム実行後のリストの並びとして正しいものはどれか。
クラス ListElement
メンバ変数 val: 文字列型
メンバ変数 next: ListElement型
○手続名: deleteSecond(ListElement: head)
ListElement: p1
ListElement: p2
/* p1は1番目の要素(A)を指す */
p1 ← head.next
/* p2は2番目の要素(B)を指す */
p2 ← p1.next
/* リンクのつなぎ替え */
p1.next ← p2.next【解答】
[Head] -> [A] -> [C]
【解説】
単方向リストの削除操作のトレースです。
- 初期状態:
Head -> A -> B -> C p1 ← head.next:p1は A を指します。p2 ← p1.next:p1(A) の次は B なので、p2は B を指します。p1.next ← p2.next:p2(B) のnextは C です。p1(A) のnextを C に書き換えます。
- 結果、A の次は直接 C を指すようになり、B はリストから外れます。
Head -> A -> C
【Javaコード】
class ListElement {
String val;
ListElement next;
ListElement(String val) { this.val = val; }
}
public class Main {
public static void main(String[] args) {
// リストの構築: Head -> A -> B -> C
ListElement head = new ListElement("Head");
ListElement a = new ListElement("A");
ListElement b = new ListElement("B");
ListElement c = new ListElement("C");
head.next = a;
a.next = b;
b.next = c;
// 削除処理
ListElement p1 = head.next; // A
ListElement p2 = p1.next; // B
p1.next = p2.next; // A -> C
// 結果確認
System.out.println(head.next.val + " -> " + head.next.next.val);
}
}
第17問:2分木の巡回(再帰)
【問題】
次のプログラムは、2分木を巡回してノードの値を出力する関数 traverse である。
下図の構造を持つ木に対して traverse(Node1) を実行したとき、出力される順番はどれか。
※ nil は子が何もないことを表す。
[木の構造]
1
/ \
2 3
/
4クラス Node
メンバ変数 val: 整数型
メンバ変数 left: Node型
メンバ変数 right: Node型
○手続名: traverse(Node: n)
if (n = nil)
return
endif
traverse(n.left)
n.val を出力
traverse(n.right)
【解答】
4, 2, 1, 3
【解説】
これは「通りがけ順(In-order)」の巡回アルゴリズムです。
処理順序:左の部分木 → 自分 → 右の部分木
traverse(1)開始traverse(1.left)つまりtraverse(2)を呼び出し。traverse(2.left)つまりtraverse(4)を呼び出し。traverse(4.left)は nil なのでリターン。- 4 を出力。
traverse(4.right)は nil なのでリターン。
traverse(4)終了。- 2 を出力。
traverse(2.right)は nil なのでリターン。
traverse(2)終了。- 1 を出力。
traverse(1.right)つまりtraverse(3)を呼び出し。traverse(3.left)は nil なのでリターン。- 3 を出力。
traverse(3.right)は nil なのでリターン。
- 出力順序は 4 → 2 → 1 → 3 となります。
【Javaコード】
class Node {
int val;
Node left, right;
Node(int val) { this.val = val; }
}
public class Main {
public static void main(String[] args) {
Node n1 = new Node(1);
Node n2 = new Node(2);
Node n3 = new Node(3);
Node n4 = new Node(4);
n1.left = n2; n1.right = n3;
n2.left = n4;
traverse(n1);
}
static void traverse(Node n) {
if (n == null) return;
traverse(n.left);
System.out.println(n.val);
traverse(n.right);
}
}
第18問:ビット演算(論理積とシフト)
【問題】
次のプログラム中の関数 getBit(20, 3) を呼び出したとき、戻り値はいくつか。
※整数 20 の2進数表現は 10100 である。
※ << は左シフト、and はビットごとの論理積を表す。
○整数型: getBit(整数型: val, 整数型: n)
整数型: mask ← 1
/* 1 を (n-1) ビット左にシフトする */
mask ← mask << (n - 1)
if ((val and mask) ≠ 0)
return 1
else
return 0
endif【解答】
1
【解説】
指定されたビット位置(下位からn番目)が 1 か 0 かを判定する処理です。
引数: val = 20 (10100), n = 3
maskの初期値は1(00001)。maskを3 - 1 = 2ビット左シフトします。00001<< 2 =00100(10進数で4)
valとmaskの論理積 (and) をとります。10100(20)00100(4)- and ↓
00100(4)
- 結果は 4 であり、0 ではないため、if 文が真となり 1 を返します。これは、下位から3番目のビットが立っていることを意味します。
【Javaコード】
public class Main {
public static void main(String[] args) {
System.out.println(getBit(20, 3));
}
static int getBit(int val, int n) {
int mask = 1;
mask = mask << (n - 1);
if ((val & mask) != 0) {
return 1;
} else {
return 0;
}
}
}
第19問:クラスとインスタンス(状態管理)
【問題】
次のプログラムを実行したとき、出力される値はいくつか。
クラス Counter
メンバ変数 count: 整数型
○Counter() /* コンストラクタ */
count ← 0
○add(整数型: n)
count ← count + n
○clear()
count ← 0
/* メイン処理 */
Counter: c1 ← Counter()
Counter: c2 ← Counter()
c1.add(5)
c2.add(10)
c1.add(3)
c2.clear()
c2.add(2)
(c1.count + c2.count) を出力【解答】
10
【解説】
オブジェクト指向における「インスタンスごとの独立性」を問う問題です。
c1 と c2 は別のメモリ領域を持つため、互いの count は干渉しません。
c1.add(5):c1.countは 5。c2.add(10):c2.countは 10。c1.add(3):c1.countは 5 + 3 = 8。c2.clear():c2.countは 0。c2.add(2):c2.countは 0 + 2 = 2。- 出力:
c1.count(8) +c2.count(2) = 10。
【Javaコード】
class Counter {
int count;
Counter() { count = 0; }
void add(int n) { count += n; }
void clear() { count = 0; }
}
public class Main {
public static void main(String[] args) {
Counter c1 = new Counter();
Counter c2 = new Counter();
c1.add(5);
c2.add(10);
c1.add(3);
c2.clear();
c2.add(2);
System.out.println(c1.count + c2.count);
}
}
第20問:選択ソート(アルゴリズムの穴埋め)
【問題】
次のプログラムは、配列 data を昇順に並べ替える(ソートする)ものである。
プログラムを実行して正しくソートするために、 [ 空欄 ] に入れるべき適切な式はどれか。
整数型の配列: data ← {4, 3, 1, 5, 2}
整数型: i, j, minIndex, temp
for (i を 1 から dataの要素数 - 1 まで 1 ずつ増やす)
minIndex ← i
/* 未ソート部分から最小値の位置を探す */
for (j を i + 1 から dataの要素数 まで 1 ずつ増やす)
if ( [ 空欄 ] )
minIndex ← j
endif
endfor
/* 値の交換 */
temp ← data[i]
data[i] ← data[minIndex]
data[minIndex] ← temp
endfor【解答】
data[j] < data[minIndex]
【解説】
選択ソート(Selection Sort)のアルゴリズムです。
- 外側のループ
iは、ソート済み位置の境界を決めます。 - 内側のループ
jで、i以降の範囲(未ソート部分)から「最小値」を探します。 - 現在の暫定的な最小値
data[minIndex]よりも、さらに小さい値data[j]が見つかった場合、minIndexをjに更新する必要があります。 - したがって、条件式は「新しい値の方が小さいか?」を問う
data[j] < data[minIndex]となります。
【Javaコード】
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] data = {4, 3, 1, 5, 2};
int minIndex, temp;
for (int i = 0; i < data.length - 1; i++) {
minIndex = i;
for (int j = i + 1; j < data.length; j++) {
// Javaは0始まりですがロジックは同じ
if (data[j] < data[minIndex]) {
minIndex = j;
}
}
// 交換
temp = data[i];
data[i] = data[minIndex];
data[minIndex] = temp;
}
System.out.println(Arrays.toString(data));
}
}
第21問:ハッシュ法(オープンアドレス法)
【問題】
次のプログラムは、ハッシュ関数 h(x) = x mod 10 を用いてデータを配列 table に格納するものである。
衝突(コリジョン)が発生した場合は、格納位置を 1 つ後ろにずらす「オープンアドレス法(線形探索法)」を用いる。
データ 15, 25, 35 を順に格納した後、データ 35 が格納されている配列の添字(インデックス)はいくつか。
※配列の初期値はすべて -1(空)とする。
整数型の配列: table ← {-1, -1, -1, ...} (要素数10)
整数型の配列: data ← {15, 25, 35}
整数型: i, pos
for (i を 1 から 3 まで 1 ずつ増やす)
pos ← data[i] mod 10
/* 空きが見つかるまで位置をずらす */
while (table[pos] ≠ -1)
pos ← (pos + 1) mod 10
endwhile
table[pos] ← data[i]
endfor
35 が格納された pos を出力
【解答】
7
【解説】
ハッシュ値の衝突処理をトレースする問題です。ハッシュ値はすべて x mod 10 で計算されます。
- 15 の格納:
- ハッシュ値:
15 mod 10 = 5。 table[5]は空なので、そこに 15 を格納します。
- ハッシュ値:
- 25 の格納:
- ハッシュ値:
25 mod 10 = 5。 table[5]には既に 15 があるため衝突します。posを 1 ずらして 6 にします。table[6]は空なので、そこに 25 を格納します。
- ハッシュ値:
- 35 の格納:
- ハッシュ値:
35 mod 10 = 5。 table[5](15) → 埋まっている。次は 6。table[6](25) → 埋まっている。次は 7。table[7]は空なので、そこに 35 を格納します。- 結果、7 が出力されます。
- ハッシュ値:
【Javaコード】
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] table = new int[10];
Arrays.fill(table, -1);
int[] data = {15, 25, 35};
int finalPos = -1;
for (int x : data) {
int pos = x % 10;
while (table[pos] != -1) {
pos = (pos + 1) % 10;
}
table[pos] = x;
if (x == 35) finalPos = pos;
}
System.out.println(finalPos);
}
}
第22問:マージソート(併合処理)
【問題】
次のプログラムは、昇順に整列済みの2つの配列 arrA と arrB を、昇順を保ったまま1つの配列 arrC にまとめる(マージする)処理である。
プログラム実行直後、arrC の 4番目 の要素に格納されている値はいくつか。
整数型の配列: arrA ← {2, 5, 9}
整数型の配列: arrB ← {1, 6, 8}
整数型の配列: arrC ← {} (要素数6の空配列)
整数型: a ← 1, b ← 1, c ← 1
while (a ≤ 3 and b ≤ 3)
if (arrA[a] < arrB[b])
arrC[c] ← arrA[a]
a ← a + 1
else
arrC[c] ← arrB[b]
b ← b + 1
endif
c ← c + 1
endwhile
/* 残りの要素をコピーする処理は省略 */
arrC[4] を出力
【解答】
6
【解説】
2つの配列の先頭同士を比較し、小さい方を採用していく処理です。
- 1回目:
A:2vsB:1。 1 (B) を採用。arrC={1},bが進む。 - 2回目:
A:2vsB:6。 2 (A) を採用。arrC={1, 2},aが進む。 - 3回目:
A:5vsB:6。 5 (A) を採用。arrC={1, 2, 5},aが進む。 - 4回目:
A:9vsB:6。 6 (B) を採用。arrC={1, 2, 5, 6},bが進む。- ここで
arrCの4番目の要素が決まりました。値は 6 です。
- ここで
【Javaコード】
public class Main {
public static void main(String[] args) {
int[] arrA = {2, 5, 9};
int[] arrB = {1, 6, 8};
int[] arrC = new int[6];
int a = 0, b = 0, c = 0;
// 擬似言語の1始まりを0始まりに読み替え
while (a < arrA.length && b < arrB.length) {
if (arrA[a] < arrB[b]) {
arrC[c++] = arrA[a++];
} else {
arrC[c++] = arrB[b++];
}
}
// Java配列は0から始まるため、4番目はインデックス3
System.out.println(arrC[3]);
}
}
第23問:ビット操作と論理演算(XOR)
【問題】
次のプログラムを実行したとき、出力される値はいくつか。
※ xor は排他的論理和を表し、2進数でビットごとに演算を行う。
整数型: a ← 10
整数型: b ← 6
整数型: temp
/* 値の交換(XORスワップアルゴリズムの一部) */
temp ← a xor b
a ← temp
temp ← a xor b
/* ここでの temp の値を出力 */
temp を出力
【解答】
10
【解説】
ビット演算の性質を問う問題です。
10進数の10は2進数で 1010、6は 0110 です。
- 最初の
temp:1010(10) xor0110(6) =1100(12)。aに 12 が代入されます。
- 次の
temp:- 現在の
aは 12 (1100)。 bは変わらず 6 (0110)。1100(12) xor0110(6) =1010(10)。- 結果として、元の
aの値である 10 が出力されます。
- 現在の
【Javaコード】
public class Main {
public static void main(String[] args) {
int a = 10; // 1010
int b = 6; // 0110
int temp = a ^ b; // 1100 (12)
a = temp;
temp = a ^ b; // 1100 ^ 0110 = 1010 (10)
System.out.println(temp);
}
}
第24問:無向グラフの隣接行列
【問題】
次の2次元配列 adj は、ノード1から3までの無向グラフの接続関係を表している(1は接続あり、0は接続なし)。
ノード2に接続されている辺の本数(次数)はいくつか。
整数型の配列: adj ← {
{0, 1, 1},
{1, 0, 1},
{1, 1, 0}
}
整数型: count ← 0
整数型: i
/* ノード2(配列の2行目)の接続数をカウント */
for (i を 1 から 3 まで 1 ずつ増やす)
if (adj[2][i] = 1)
count ← count + 1
endif
endfor
count を出力
【解答】
2
【解説】
隣接行列における次数(つながっている線の数)の計算問題です。
ノード2に対応するのは配列の2行目(擬似言語では添字2、Java等では添字1)です。
- 配列
adjの2行目は{1, 0, 1}です。 i=1:adj[2][1]は 1。(カウント+1)i=2:adj[2][2]は 0。(自分自身へのループなし)i=3:adj[2][3]は 1。(カウント+1)- 合計で 2 となります。
【Javaコード】
public class Main {
public static void main(String[] args) {
int[][] adj = {
{0, 1, 1},
{1, 0, 1},
{1, 1, 0}
};
int count = 0;
// ノード2に対応するのはインデックス1
for (int i = 0; i < 3; i++) {
if (adj[1][i] == 1) {
count++;
}
}
System.out.println(count);
}
}
第25問:双方向リスト(ノードの削除)
【問題】
次のプログラムは、配列を用いて実装された双方向リストから、要素 20 を削除する処理である。
処理終了後、要素 10 の「次の要素」を指す next 配列の値はいくつになっているか。
※ prev は前の要素の添字、next は次の要素の添字を表す。
/* インデックス: 1 2 3 */
/* 値(data): 10, 20, 30 */
整数型の配列: next ← {2, 3, -1}
整数型の配列: prev ← {-1, 1, 2}
整数型: deleteIdx ← 2 /* 削除する要素(20)の添字 */
整数型: p ← prev[deleteIdx] /* 1 (10の場所) */
整数型: n ← next[deleteIdx] /* 3 (30の場所) */
/* 削除処理:ポインタの付け替え */
if (p ≠ -1)
next[p] ← n
endif
if (n ≠ -1)
prev[n] ← p
endif
next[1] を出力
【解答】
3
【解説】
双方向リストの削除操作では、削除するノードの前後のノードを直接つなぎ合わせます。
- 初期状態:
- ノード1(10)の次は ノード2(20)。 (
next[1] = 2) - ノード2(20)の次は ノード3(30)。
- ノード3(30)の前は ノード2(20)。
- ノード1(10)の次は ノード2(20)。 (
- 削除対象:
deleteIdx = 2(値20)。- 前のノード
pは 1。 - 次のノード
nは 3。
- 前のノード
- 付け替え:
next[p] ← n: 「前のノード(1)の次」を、「次のノード(3)」に書き換えます。- つまり
next[1]が 3 になります。 prev[n] ← p: 「次のノード(3)の前」を、「前のノード(1)」に書き換えます。
- 結果:
next[1]の値は 3 です。これでリストは 10 → 30 と繋がりました。
【Javaコード】
public class Main {
public static void main(String[] args) {
// インデックス0は使わず、1,2,3を使用すると仮定
int[] next = {0, 2, 3, -1};
int[] prev = {0, -1, 1, 2};
int deleteIdx = 2;
int p = prev[deleteIdx]; // 1
int n = next[deleteIdx]; // 3
if (p != -1) next[p] = n;
if (n != -1) prev[n] = p;
System.out.println(next[1]);
}
}
第26問:バブルソート(隣接交換法)
【問題】
次のプログラムは、配列を昇順に整列するバブルソートである。
外側のループ(変数 i)が 1回終了した時点 で、配列 nums の内容はどのように変化しているか。
整数型の配列: nums ← {4, 2, 5, 1, 3}
整数型: i, j, temp
/* 末尾から先頭に向かって順に比較・交換を行う */
for (i を 1 から numsの要素数 - 1 まで 1 ずつ増やす)
for (j を numsの要素数 から i + 1 まで 1 ずつ減らす)
if (nums[j] < nums[j - 1])
/* 交換処理 */
temp ← nums[j]
nums[j] ← nums[j - 1]
nums[j - 1] ← temp
endif
endfor
endfor
nums を出力【解答】
{1, 4, 2, 5, 3}
【解説】
バブルソートのこの実装は、後ろから前へ向かって小さい値を「泡のように」浮かび上がらせる方式です。
- i = 1 のループ(先頭 nums[1] を確定させるパス)
- j = 5: nums[5] (3) と nums[4] (1) を比較。3 > 1 なので交換しない。
- j = 4: nums[4] (1) と nums[3] (5) を比較。1 < 5 なので交換。配列は
{4, 2, 1, 5, 3}。 - j = 3: nums[3] (1) と nums[2] (2) を比較。1 < 2 なので交換。配列は
{4, 1, 2, 5, 3}。 - j = 2: nums[2] (1) と nums[1] (4) を比較。1 < 4 なので交換。配列は
{1, 4, 2, 5, 3}。
- 結果
- 最も小さい値 1 が先頭に移動し、残りの並びは交換過程の結果に従います。
【Javaコード】
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] nums = {4, 2, 5, 1, 3};
// 外側のループ1回分 (i=0に対応)
for (int j = nums.length - 1; j > 0; j--) {
if (nums[j] < nums[j - 1]) {
int temp = nums[j];
nums[j] = nums[j - 1];
nums[j - 1] = temp;
}
}
System.out.println(Arrays.toString(nums));
}
}
第27問:挿入ソート
【問題】
次のプログラムは、配列を昇順に整列する挿入ソートの途中経過である。
変数 i が 3 のときのループ処理が終了した直後、配列 cards の内容はどれか。
整数型の配列: cards ← {10, 5, 8, 3, 6}
整数型: i, j, temp
/* 2番目の要素から順に、適切な位置に挿入していく */
for (i を 2 から cardsの要素数 まで 1 ずつ増やす)
temp ← cards[i]
j ← i - 1
while (j ≥ 1 and cards[j] > temp)
cards[j + 1] ← cards[j]
j ← j - 1
endwhile
cards[j + 1] ← temp
endfor【解答】
{5, 8, 10, 3, 6}
【解説】
挿入ソートは、左側の「整列済み部分」に対して、新しい要素を適切な場所に差し込むアルゴリズムです。
- i = 2 のとき(値 5 の処理):
- 10 と 5 を比較し、5 を先頭に挿入。
- 配列は
{5, 10, 8, 3, 6}となる。
- i = 3 のとき(値 8 の処理):
- 整列済み部分
{5, 10}に対して、8 を適切な場所に挿入します。 - 10 > 8 なので、10 を右にずらす。
- 5 < 8 なので、5 の後ろ(10があった場所)に 8 を入れる。
- 配列は
{5, 8, 10, 3, 6}となる。 - ※後ろの
3, 6はまだ手付かずです。
- 整列済み部分
【Javaコード】
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] cards = {10, 5, 8, 3, 6};
// Javaでは i=1 が擬似言語の i=2 に相当
for (int i = 1; i <= 2; i++) { // i=2(3番目の要素)まで実行
int temp = cards[i];
int j = i - 1;
while (j >= 0 && cards[j] > temp) {
cards[j + 1] = cards[j];
j--;
}
cards[j + 1] = temp;
}
System.out.println(Arrays.toString(cards));
}
}第28問:パリティビットと誤り検出
【問題】
次のプログラムは、7ビットのデータに「偶数パリティビット」を付加して8ビットにする処理である。
入力データ data が 1011001 のとき、出力される parity の値(0 または 1)はいくつか。
※偶数パリティとは、データとパリティビットを合わせた「1」の個数が偶数になるようにする方式である。
/* data は整数の配列として各ビットを表す {1, 0, 1, 1, 0, 0, 1} */
整数型の配列: data ← {1, 0, 1, 1, 0, 0, 1}
整数型: sum ← 0
整数型: k, parity
for (k を 1 から 7 まで 1 ずつ増やす)
sum ← sum + data[k]
endfor
if (sum mod 2 = 0)
parity ← 0
else
parity ← 1
endif
parity を出力【解答】
0
【解説】
偶数パリティでは、「1の総数」が偶数ならパリティビットは0、奇数なら1になります(そうすることで全体の1の数を偶数にするため)。
- 配列
dataの要素を合計します。1 + 0 + 1 + 1 + 0 + 0 + 1= 4
- 合計
sumは 4 です。 4 mod 2は 0(偶数)です。- 条件分岐
if (sum mod 2 = 0)が真となるため、parityには 0 が代入されます。
【Javaコード】
public class Main {
public static void main(String[] args) {
int[] data = {1, 0, 1, 1, 0, 0, 1};
int sum = 0;
for (int bit : data) {
sum += bit;
}
int parity;
if (sum % 2 == 0) {
parity = 0;
} else {
parity = 1;
}
System.out.println(parity);
}
}
第29問:グラフ走査(深さ優先探索・再帰)
【問題】
次のプログラムは、ノード1からスタートして、接続されているノードを「深さ優先探索 (DFS)」で順に訪問するものである。
visit(1) を呼び出したとき、出力される数値の順序はどれか。
※ adj は隣接リストを表し、adj[1] はノード1から行けるノードのリストである。
配列の配列: adj ← {
{}, /* index 0: 未使用 */
{2, 3}, /* index 1: 1から2と3へ */
{4}, /* index 2: 2から4へ */
{}, /* index 3: 行き先なし */
{} /* index 4: 行き先なし */
}
○visit(整数型: n)
n を出力
整数型: i
for (i を 1 から adj[n]の要素数 まで 1 ずつ増やす)
visit(adj[n][i])
endfor【解答】
1 → 2 → 4 → 3
【解説】
深さ優先探索(DFS)は、行けるところまで奥へ進み、行き止まりになったら戻る(バックトラック)アルゴリズムです。
- visit(1) 呼び出し。 1 を出力。
- 隣接リスト
adj[1]は{2, 3}。まずは先頭の 2 へ進む。
- 隣接リスト
- visit(2) 呼び出し。 2 を出力。
- 隣接リスト
adj[2]は{4}。 4 へ進む。
- 隣接リスト
- visit(4) 呼び出し。 4 を出力。
- 隣接リスト
adj[4]は空。関数終了し、visit(2)へ戻る。 visit(2)もこれ以上行く場所がないので終了し、visit(1)へ戻る。
- 隣接リスト
- visit(1) の続き
- さきほどは 2 へ行ったので、次は 3 へ進む。
- visit(3) 呼び出し。 3 を出力。
- 隣接リスト
adj[3]は空。関数終了。
- 隣接リスト
結果、順序は 1, 2, 4, 3 となります。
【Javaコード】
public class Main {
static int[][] adj = {
{},
{2, 3},
{4},
{},
{}
};
public static void main(String[] args) {
visit(1);
}
public static void visit(int n) {
System.out.println(n);
for (int neighbor : adj[n]) {
visit(neighbor);
}
}
}
第30問:状態遷移(オートマトン)
【問題】
次のプログラムは、入力された数値に応じて状態(state)を変化させる処理である。
配列 inputs {1, 0, 1, 1} を順に処理した後、最終的な state の値はいくつか。
※ transition[現在の状態][入力値] で次の状態が決まるものとする。
/* 状態遷移表: transition[state][input] */
整数型の配列: transition ← {
{0, 1}, /* state 0 のとき: 入力0なら0, 入力1なら1へ */
{2, 1}, /* state 1 のとき: 入力0なら2, 入力1なら1へ */
{0, 2} /* state 2 のとき: 入力0なら0, 入力1なら2へ */
}
整数型の配列: inputs ← {1, 0, 1, 1}
整数型: state ← 0
整数型: k
for (k を 1 から inputsの要素数 まで 1 ずつ増やす)
state ← transition[state][inputs[k] + 1]
/* ※擬似言語の配列添字は1始まりのため、input(0/1)に+1して列を指定 */
endfor
state を出力【解答】
2
【解説】
表に従って状態を遷移させます。
- 初期状態:
state = 0 - 1つ目の入力 (1):
transition[0][1](※入力1に対応する列) を参照 → 次の状態は 1。 - 2つ目の入力 (0):
state = 1。transition[1][0]を参照 → 次の状態は 2。 - 3つ目の入力 (1):
state = 2。transition[2][1]を参照 → 次の状態は 2。 - 4つ目の入力 (1):
state = 2。transition[2][1]を参照 → 次の状態は 2。 - 最終的な状態は 2 となります。
【Javaコード】
public class Main {
public static void main(String[] args) {
// Javaは0始まりなのでそのまま使用
int[][] transition = {
{0, 1}, // state 0
{2, 1}, // state 1
{0, 2} // state 2
};
int[] inputs = {1, 0, 1, 1};
int state = 0;
for (int input : inputs) {
state = transition[state][input];
}
System.out.println(state);
}
}
第31問:クイックソート(パーティション分割)
【問題】
次のプログラムは、クイックソートの一部である「パーティション分割」を行う関数 partition である。
配列 data の最後の要素を「基準値(ピボット)」とし、それより小さい値を左側、大きい値を右側に集める処理を行う。
プログラム実行直後、配列 data の内容はどのように変化しているか。
整数型の配列: data ← {2, 6, 1, 7, 4}
整数型: pivot ← data[dataの要素数] /* ピボットは最後の 4 */
整数型: i ← 0 /* 小さい要素の境界線 */
整数型: j, temp
/* 1番目から (要素数-1) 番目まで走査 */
for (j を 1 から dataの要素数 - 1 まで 1 ずつ増やす)
if (data[j] < pivot)
i ← i + 1
/* data[i] と data[j] を交換 */
temp ← data[i]
data[i] ← data[j]
data[j] ← temp
endif
endfor
/* 最後にピボットを境界線の隣に移動 */
temp ← data[i + 1]
data[i + 1] ← data[dataの要素数]
data[dataの要素数] ← temp
data を出力
【解答】
{2, 1, 4, 7, 6}
【解説】
ピボット(4)より小さい値を配列の前方に詰め込み、最後にピボットをその境界に置く処理です。
- 初期状態:
i=0,pivot=4, 配列{2, 6, 1, 7, 4}。 - j=1:
2 < 4なので、iを1増やしてi=1。data[1](2) とdata[1](2) を交換(変化なし)。 - j=2:
6 > 4なので何もしない。 - j=3:
1 < 4なので、iを1増やしてi=2。data[2](6) とdata[3](1) を交換。配列は{2, 1, 6, 7, 4}。 - j=4:
7 > 4なので何もしない。 - ループ終了後: data[i+1] つまり data[3](6) と、末尾のピボット(4)を交換。配列は {2, 1, 4, 7, 6} となります。これで、4より左は小さく、右は大きくなりました。
【Javaコード】
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] data = {2, 6, 1, 7, 4};
int pivot = data[data.length - 1]; // pivot = 4
int i = -1; // Javaは0始まりなので、擬似言語の0に対応するのは-1
for (int j = 0; j < data.length - 1; j++) {
if (data[j] < pivot) {
i++;
int temp = data[i];
data[i] = data[j];
data[j] = temp;
}
}
// ピボットの移動
int temp = data[i + 1];
data[i + 1] = data[data.length - 1];
data[data.length - 1] = temp;
System.out.println(Arrays.toString(data));
}
}
第32問:ゲーム木の探索(ミニマックス法)
【問題】
次のプログラムは、ゲーム木のスコアを計算する再帰関数 minimax である。
minimax(0, true) を呼び出したとき、戻り値はいくつか。
※引数 isMax が true のときは自分の手番(最大値を選ぶ)、false のときは相手の手番(最小値を選ぶ)を表す。
/* 木構造のデータ(葉のスコア)を配列で表現 */
/* index 0 は根。1,2 はその子。3,4,5,6 は葉 */
整数型の配列: scores ← {0, 0, 0, 5, 3, 8, 1}
○整数型: minimax(整数型: node, ブール型: isMax)
/* 葉ノード(これ以上子がない)なら値を返す */
if (node ≥ 3)
return scores[node]
endif
if (isMax = true)
/* 自分: 子ノード(左:2*node+1, 右:2*node+2)のうち大きい方を選ぶ */
return max(minimax(2 * node + 1, false), minimax(2 * node + 2, false))
else
/* 相手: 小さい方を選ぶ */
return min(minimax(2 * node + 1, true), minimax(2 * node + 2, true))
endif
【解答】
3
【解説】
自分はスコアを最大化、相手は最小化するように動くと仮定して、根(ルート)の値を求める問題です。
- 葉の値: ノード3=5, ノード4=3, ノード5=8, ノード6=1。
- ノード1(相手の番: node 3, 4 から最小を選択):
- min(5, 3) = 3
- ノード2(相手の番: node 5, 6 から最小を選択):
- min(8, 1) = 1
- ノード0(自分の番: node 1, 2 から最大を選択):
- max(3, 1) = 3
- したがって、戻り値は 3 です。
【Javaコード】
public class Main {
static int[] scores = {0, 0, 0, 5, 3, 8, 1}; // index 0~6
public static void main(String[] args) {
System.out.println(minimax(0, true));
}
public static int minimax(int node, boolean isMax) {
// 簡易的な判定:配列外なら終了とみなす等の処理が必要だが
// ここでは問題文の通りnode>=3で葉とする
if (node >= 3) {
return scores[node];
}
if (isMax) {
int left = minimax(2 * node + 1, false);
int right = minimax(2 * node + 2, false);
return Math.max(left, right);
} else {
int left = minimax(2 * node + 1, true);
int right = minimax(2 * node + 2, true);
return Math.min(left, right);
}
}
}
第33問:ナップサック問題(動的計画法)
【問題】
次のプログラムは、重さの上限(capacity)まで荷物を入れたときの価値の最大値を求めるものである。
プログラム実行後、出力される dp[5] の値はいくつか。
※ dp[w] は、重さ w まで入るリュックの最大価値を表す。
整数型の配列: weights ← {2, 3} /* 荷物の重さ */
整数型の配列: values ← {3, 4} /* 荷物の価値 */
整数型の配列: dp ← {0, 0, 0, 0, 0, 0} /* 重さ0〜5に対応 */
整数型: capacity ← 5
整数型: i, w
/* 荷物の種類ごとにDPテーブルを更新 */
for (i を 1 から 2 まで 1 ずつ増やす)
/* 重い方から更新することで、同じ荷物の重複利用を防ぐ(0/1ナップサック) */
for (w を capacity から weights[i] まで 1 ずつ減らす)
if (dp[w] < dp[w - weights[i]] + values[i])
dp[w] ← dp[w - weights[i]] + values[i]
endif
endfor
endfor
dp[5] を出力
【解答】
7
【解説】
動的計画法(DP)を用いて、価値の最大値を積み上げていく問題です。
- 初期状態:
dpはすべて 0。 - 荷物1 (重さ2, 価値3) の処理:
w=5:dp[5]vsdp[3]+3→0vs3→3w=4:dp[4]vsdp[2]+3→3w=3:dp[3]vsdp[1]+3→3w=2:dp[2]vsdp[0]+3→3- 配列状態:
{0, 0, 3, 3, 3, 3}
- 荷物2 (重さ3, 価値4) の処理:
w=5:dp[5](3) vsdp[5-3](3) + 4 →3vs7→ 7 に更新。w=4:dp[4](3) vsdp[4-3](0) + 4 →3vs4→ 4 に更新。w=3:dp[3](3) vsdp[3-3](0) + 4 →3vs4→ 4 に更新。- 配列状態:
{0, 0, 3, 4, 4, 7}
dp[5]の値である 7 が出力されます。
【Javaコード】
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
int[] weights = {2, 3};
int[] values = {3, 4};
int capacity = 5;
int[] dp = new int[capacity + 1]; // 0~5
for (int i = 0; i < weights.length; i++) {
int weight = weights[i];
int value = values[i];
// 重複なし(0/1)のため、後ろからループ
for (int w = capacity; w >= weight; w--) {
if (dp[w] < dp[w - weight] + value) {
dp[w] = dp[w - weight] + value;
}
}
}
System.out.println(dp[5]);
}
}
第34問:多項式の計算(ホーナー法)
【問題】
次のプログラムは、多項式 の値を、
のときについて計算するものである。
このアルゴリズム(ホーナー法)を用いたとき、出力される result の値はいくつか。
整数型の配列: coef ← {3, 2, 1, 5} /* 係数: 3x^3 + 2x^2 + 1x + 5 */
整数型: x ← 2
整数型: result ← coef[1] /* 最上位の係数(3)で初期化 */
整数型: i
for (i を 2 から 4 まで 1 ずつ増やす)
result ← result × x + coef[i]
endfor
result を出力【解答】
39
【解説】
ホーナー法は、多項式を の形に変形して計算する方法です。乗算の回数を減らせます。
- 初期化:
result = 3 - i=2 (係数2):
result = 3 * 2 + 2 = 8- (現在の式:
)
- i=3 (係数1):
result = 8 * 2 + 1 = 17- (現在の式:
)
- i=4 (係数5):
result = 17 * 2 + 5 = 34 + 5 = 39- (現在の式:
)
- 結果 39 が出力されます。
【Javaコード】
public class Main {
public static void main(String[] args) {
int[] coef = {3, 2, 1, 5};
int x = 2;
int result = coef[0]; // 最上位の係数
for (int i = 1; i < coef.length; i++) {
result = result * x + coef[i];
}
System.out.println(result);
}
}
第35問:繰り返し二乗法(べき乗の剰余)
【問題】
次のプログラムは、べき乗 を高速に計算する「繰り返し二乗法」のアルゴリズムである。
を計算したとき、出力される ans の値はいくつか。
※演算子 mod は剰余、div は整数の商を表す。
整数型: base ← 2
整数型: exp ← 10 /* 2進数で 1010 */
整数型: mod ← 1000
整数型: ans ← 1
while (exp > 0)
/* 指数の最下位ビットが 1 なら現在の base を掛ける */
if (exp mod 2 = 1)
ans ← (ans × base) mod mod
endif
/* base を二乗して、指数を右にシフト */
base ← (base × base) mod mod
exp ← exp div 2
endwhile
ans を出力
【解答】
【解答】
24
【解説】
なので、1000で割った余りは 24 です。これをアルゴリズム通りにトレースします。
- 初期:
base=2,exp=10 (1010),ans=1 - ループ1回目:
exp mod 2は 0。ansそのまま。base更新:。
exp更新:(
0101)。
- ループ2回目:
exp mod 2は 1。ans更新:。
base更新:。
exp更新:(
0010)。
- ループ3回目:
exp mod 2は 0。ansそのまま(4)。base更新:。
exp更新:(
0001)。
- ループ4回目:
exp mod 2は 1。ans更新:。
。
ansは 24。base更新:(不要)
exp更新:。
- ループ終了。24 が出力されます。
【Javaコード】
public class Main {
public static void main(String[] args) {
long base = 2;
long exp = 10;
long mod = 1000;
long ans = 1;
while (exp > 0) {
if (exp % 2 == 1) {
ans = (ans * base) % mod;
}
base = (base * base) % mod;
exp = exp / 2;
}
System.out.println(ans);
}
}
第36問:Diffie-Hellman鍵交換法
【問題】
次のプログラムは、2者間(AliceとBob)で共通の秘密鍵を生成する「Diffie-Hellman鍵交換法」のアルゴリズムである。
Aliceの秘密値 secretA が 3、Bobの秘密値 secretB が 2 のとき、最終的に共有される鍵 keyA(または keyB)の値はいくつか。
※ modPow(base, exp, mod) は を計算する関数とする。
整数型: p ← 13 /* 素数 */
整数型: g ← 6 /* 生成元 */
整数型: secretA ← 3
整数型: secretB ← 2
整数型: publicA, publicB, keyA, keyB
/* 公開鍵の生成と交換 */
publicA ← modPow(g, secretA, p)
publicB ← modPow(g, secretB, p)
/* 相手の公開鍵と自分の秘密値から共通鍵を生成 */
keyA ← modPow(publicB, secretA, p)
keyB ← modPow(publicA, secretB, p)
keyA を出力
【解答】
12
【解説】
互いに相手の公開鍵を自分の秘密値で累乗することで、同じ値を共有できる仕組みです。
- Aliceの公開鍵:
。 (
publicA= 8) - Bobの公開鍵:
。 (
publicB= 10) - 共通鍵生成 (Alice側):
。
余り 12。
- 確認 (Bob側):
。
余り 12。
- 両者とも 12 が生成されます。
【Javaコード】
public class Main {
public static void main(String[] args) {
long p = 13;
long g = 6;
long secretA = 3;
long secretB = 2;
long publicA = modPow(g, secretA, p); // 8
long publicB = modPow(g, secretB, p); // 10
long keyA = modPow(publicB, secretA, p); // 10^3 % 13 = 12
System.out.println(keyA);
}
public static long modPow(long base, long exp, long mod) {
long res = 1;
for (int i = 0; i < exp; i++) {
res = (res * base) % mod;
}
return res;
}
}
第37問:ヒープ構造(最大ヒープへの追加)
【問題】
次のプログラムは、配列 heap を「最大ヒープ(親は子より大きい)」として維持しながら要素を追加する関数 pushHeap である。
空のヒープに対し、データ 10, 30, 20, 5, 40 を順に追加した直後、配列 heap の先頭(根)の要素はいくつか。
整数型の配列: heap ← {}
整数型: size ← 0
○pushHeap(整数型: val)
size ← size + 1
整数型: k ← size
heap[k] ← val
/* 親と比較して、親より大きければ交換(アップヒープ) */
while (k > 1 and heap[k] > heap[k ÷ 2])
整数型: parent ← k ÷ 2
整数型: temp ← heap[k]
heap[k] ← heap[parent]
heap[parent] ← temp
k ← parent
endwhile
【解答】
40
【解説】
最大ヒープでは、常に「最大の要素」が根(配列の先頭)に来ます。追加されるたびに親と比較して浮上していきます。
- push(10):
{10}。根は10。 - push(30): 末尾に追加
{10, 30}。30 > 10 なので交換。{30, 10}。根は30。 - push(20): 末尾に追加
{30, 10, 20}。20 < 30 (親) なのでそのまま。根は30。 - push(5): 末尾に追加
{30, 10, 20, 5}。5 < 10 (親) なのでそのまま。根は30。 - push(40): 末尾に追加
{30, 10, 20, 5, 40}。- 親(10)と比較: 40 > 10 なので交換 →
{30, 40, 20, 5, 10}。 - 親(30)と比較: 40 > 30 なので交換 →
{40, 30, 20, 5, 10}。 - 根まで到達したため終了。根は 40 です。
- 親(10)と比較: 40 > 10 なので交換 →
【Javaコード】
import java.util.ArrayList;
import java.util.List;
public class Main {
// 1始まりのインデックスを模倣するためにダミー要素を入れる
static List<Integer> heap = new ArrayList<>();
public static void main(String[] args) {
heap.add(0); // index 0 (dummy)
int[] data = {10, 30, 20, 5, 40};
for (int val : data) {
pushHeap(val);
}
System.out.println(heap.get(1)); // 根を出力
}
public static void pushHeap(int val) {
heap.add(val);
int k = heap.size() - 1;
while (k > 1 && heap.get(k) > heap.get(k / 2)) {
int parent = k / 2;
int temp = heap.get(k);
heap.set(k, heap.get(parent));
heap.set(parent, temp);
k = parent;
}
}
}
第38問:逆ポーランド記法(後置記法)
【問題】
次のプログラムは、逆ポーランド記法で書かれた配列 tokens を計算するものである。
tokens が {"3", "4", "+", "2", "*"} のとき、出力される結果はいくつか。
※ isDigit は数値かどうかを判定する関数、pop はスタックから取り出す、push は入れる処理とする。
文字列型の配列: tokens ← {"3", "4", "+", "2", "*"}
整数型のスタック: stack ← {}
整数型: a, b, i
for (i を 1 から tokensの要素数 まで 1 ずつ増やす)
if (tokens[i] が数値である)
push(stack, tokens[i]を整数に変換)
else
b ← pop(stack)
a ← pop(stack)
if (tokens[i] = "+") push(stack, a + b)
elseif (tokens[i] = "*") push(stack, a * b)
endif
endif
endfor
pop(stack) を出力
【解答】
14
【解説】
逆ポーランド記法では、数値はスタックに積み、演算子が出たら数値を2つ取り出して計算し、結果を戻します。数式で表すと (3 + 4) * 2 です。
"3": スタック[3]"4": スタック[3, 4]"+": 4, 3 を取り出し、3 + 4 = 7をプッシュ。 スタック[7]"2": スタック[7, 2]"*": 2, 7 を取り出し、7 * 2 = 14をプッシュ。 スタック[14]- 最後にポップして 14 が出力されます。
【Javaコード】
import java.util.Stack;
public class Main {
public static void main(String[] args) {
String[] tokens = {"3", "4", "+", "2", "*"};
Stack<Integer> stack = new Stack<>();
for (String token : tokens) {
if (token.matches("\\d+")) {
stack.push(Integer.parseInt(token));
} else {
int b = stack.pop();
int a = stack.pop();
if (token.equals("+")) stack.push(a + b);
else if (token.equals("*")) stack.push(a * b);
}
}
System.out.println(stack.pop());
}
}
第39問:幅優先探索(BFS)
【問題】
次のプログラムは、キューを用いてグラフを「幅優先探索 (BFS)」するものである。
ノード1から開始したとき、ノードが訪問(出力)される順序として正しいものはどれか。
※ adj は隣接リストを表す。
配列の配列: adj ← {
{}, /* 0: 未使用 */
{2, 3}, /* 1: 1から2, 3へ */
{4, 5}, /* 2: 2から4, 5へ */
{6}, /* 3: 3から6へ */
{}, {}, {} /* 4, 5, 6: 行き先なし */
}
整数型のキュー: queue ← {}
enqueue(queue, 1) /* 開始ノード */
while (queue が空でない)
整数型: n ← dequeue(queue)
n を出力
整数型: i
for (i を 1 から adj[n]の要素数 まで 1 ずつ増やす)
enqueue(queue, adj[n][i])
endfor
endwhile
【解答】
1 → 2 → 3 → 4 → 5 → 6
【解説】
深さ優先探索(DFS)とは異なり、幅優先探索(BFS)は「近いところ」から順に、同じ深さの兄弟を全て訪問してから次へ進みます。
- キュー:
{1}。 1を取り出し出力。子2, 3を追加。 キュー:{2, 3} - 2を取り出し出力。子
4, 5を追加。 キュー:{3, 4, 5} - 3を取り出し出力。子
6を追加。 キュー:{4, 5, 6} - 4を取り出し出力。子なし。 キュー:
{5, 6} - 5を取り出し出力。子なし。 キュー:
{6} - 6を取り出し出力。子なし。 キュー: {}順序は 1, 2, 3, 4, 5, 6 となります。
【Javaコード】
import java.util.LinkedList;
import java.util.Queue;
public class Main {
public static void main(String[] args) {
int[][] adj = {
{},
{2, 3},
{4, 5},
{6},
{}, {}, {}
};
Queue<Integer> queue = new LinkedList<>();
queue.add(1);
while (!queue.isEmpty()) {
int n = queue.poll();
System.out.println(n);
for (int neighbor : adj[n]) {
queue.add(neighbor);
}
}
}
}
第40問:文字列探索(力まかせ法)
【問題】
次のプログラムは、文字列 text の中に、パターン pattern が含まれているかを探す処理である。
match が true(発見)になったとき、pattern の先頭文字が一致した text 内の位置 i はいくつか。
文字列型: text ← "BABABABC"
文字列型: pattern ← "ABC"
整数型: i, j
ブール型: match ← false
for (i を 1 から (textの文字数 - patternの文字数 + 1) まで 1 ずつ増やす)
match ← true
for (j を 1 から patternの文字数 まで 1 ずつ増やす)
if (text[i + j - 1] ≠ pattern[j])
match ← false
break /* 内側のループを抜ける */
endif
endfor
if (match = true)
break /* 外側のループを抜ける */
endif
endfor
i を出力
【解答】
6
【解説】
テキストの先頭から順に、パターンと一致するかを確認します。
- i=1 ("BAB..."): "B" vs "A" → 不一致。
- i=2 ("ABA..."): "A" vs "A"(一致), "B" vs "B"(一致), "A" vs "C"(不一致)。
- i=3 ("BAB..."): "B" vs "A" → 不一致。
- i=4 ("ABA..."): "A" vs "A"(一致), "B" vs "B"(一致), "A" vs "C"(不一致)。
- i=5 ("BAB..."): "B" vs "A" → 不一致。
- i=6 ("ABC"): "A" vs "A"(一致), "B" vs "B"(一致), "C" vs "C"(一致)。
- ここで
matchがtrueのままループを完走します。 - 答えは 6 です。
- ここで
【Javaコード】
public class Main {
public static void main(String[] args) {
String text = "BABABABC";
String pattern = "ABC";
int n = text.length();
int m = pattern.length();
boolean match = false;
int foundIndex = -1;
for (int i = 0; i <= n - m; i++) {
match = true;
for (int j = 0; j < m; j++) {
if (text.charAt(i + j) != pattern.charAt(j)) {
match = false;
break;
}
}
if (match) {
foundIndex = i + 1; // 1始まりに合わせる
break;
}
}
System.out.println(foundIndex);
}
}