問1.与えられた数列を逆順に出力するプログラムを作成して下さい。
PHP
<?php
$n= intval(trim(fgets(STDIN)));
$num = explode(" ",trim(fgets(STDIN)));
$num = array_reverse($num);
echo implode(" ",$num)."\n";
?>
Java
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = Integer.parseInt(sc.next());
int[] arr = new int[n];
for (int i = 0; i < n; i++) {
arr[i] = Integer.parseInt(sc.next());
}
sc.close();
for (int j = n - 1; j >= 0; j--) {
System.out.print(arr[j]);
if (j != 0) {
System.out.print(" ");
}
}
System.out.println();
}
}
- Integer.parseInt は文字列を整数に変換するためのメソッド
問2. 不足しているカードの発見
太郎が花子と一緒にトランプ遊びをしようとしたところ、52枚あるはずのカードが n 枚のカードしか手元にありません。これらの n 枚のカードを入力として、足りないカードを出力するプログラムを作成して下さい。
太郎が最初に持っていたトランプはジョーカーを除く52枚のカードです。
-
52枚のカードは、スペード、ハート、クラブ、ダイヤの4つの絵柄に分かれており、各絵柄には13のランクがあります。
-
最初の行に太郎が持っているカードの枚数 n (n ≤ 52)が与えられます。
-
続いて n 組のカードがそれぞれ1行に与えられます。各組は1つの空白で区切られた文字と整数です。文字はカードの絵柄を表し、スペードが'S'、ハートが'H'、クラブが'C'、ダイヤが'D'で表されています。整数はそのカードのランク(1 〜 13)を表しています。
-
足りないカードをそれぞれ1行に出力して下さい。各カードは入力と同様に1つの空白で区切られた文字と整数です。出力するカードの順番は以下のとおりとします:
-
絵柄がスペード、ハート、クラブ、ダイヤの順番で優先的に出力する。
-
絵柄が同じ場合は、ランクが小さい順に出力する。
PHP
<?php
$suits = ["S", "H", "C", "D"];
$allCards = [];
foreach ($suits as $suit) {
for ($i = 1; $i <= 13; $i++) {
$allCards["$suit $i"] = true;
}
}
$n = trim(fgets(STDIN));
for ($i = 0; $i < $n; $i++) {
list($suit, $rank) = explode(" ", trim(fgets(STDIN)));
unset($allCards["$suit $rank"]);
}
foreach ($allCards as $card => $value) {
echo "$card\n";
}
?>
- 今回使いたいのは連想配列のキー→値はプレースホルダー
- foreachでas cards)としてしまうと値(true)が出力されてしまう
別解
<?php
$all_cards = [];
foreach (['S', 'H', 'C', 'D'] as $suit) {
for ($i = 1; $i <= 13; $i++) {
$all_cards[] = "$suit $i";
}
}
// 持っているカード
$n = trim(fgets(STDIN));
$have_cards = [];
for ($i = 0; $i < $n; $i++) {
$have_cards[] = trim(fgets(STDIN));
}
// 不足分を算出して出力
$missing = array_diff($all_cards, $have_cards);
foreach ($missing as $card) {
echo "$card\n";
}
?>
- array_diff() は、2つの配列を比較して、最初の配列に存在しているが、2番目の配列には存在しない要素を返す。
Java
import java.util.*;
public class Main{
public static void main(String[]args){
ArrayList<String> allCards = new ArrayList<String>();
String [] suits = {"S","H","C","D"};
for(String suit:suits){
for(int i=1;i<=13;i++){
allCards.add(suit+" "+i);
}
}
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
sc.nextLine();
Set<String> existingCards = new HashSet<>();
for(int i=0;i<num;i++){
String cards = sc.nextLine();
existingCards.add(cards);
}
for(String card:allCards){
if(!existingCards.contains(card)){
System.out.println(card);
}
}
sc.close();
}
}
//「すべてのカード」をつくる
//入力をもらう
//「手持ちのカード」をつくり入力されたカードを追加していく
//「すべてのカード」から「手持ちのカード」だけを除いて表示させる
-
Scannerは入力をトークンとして読み取るが、nextInt() は 数字の部分だけ を読み取り、改行文字 \n はバッファに残す という動作をする
→int num = sc.nextInt();で数字を読み取っても改行がバッファに残るため、その次のループ内にあるnextLine()では残った改行を読み取ってしまう
→そのため、最初の card には 何も入らず空白の出力 になってしまう。
→sc.nextLine(); を nextInt(); の直後に入れることで、改行を消費し、次の nextLine(); が正しく機能する ようにする -
ArrayListは順序付きのデータの保持に、HashSetは高速な検索と重複排除に強い
-
今回は重複してなかったらそれを表示というコードにしてるけど「重複してたら取り除く」なら以下のようなコード例になる。
-
配列は宣言時にサイズを決める必要があるため、サイズが変わらない場合に適している。
-
ArrayList(またはList)は動的にサイズを変更できるため、要素の追加・削除が必要な場合に適している。
//removeAll()を使う場合
allCards.removeAll(existingCards);
for(String card : allCards) {
System.out.println(card);
}
//イテレーターを使う場合
Iterator<String> iterator = allCards.iterator();
while(iterator.hasNext()) {
String card = iterator.next();
if(existingCards.contains(card)) {
iterator.remove();
}
}
for(String card : allCards) {
System.out.println(card);
}
別解
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
String suit = "SHCD";
// 2次元配列で各カードの存在を管理。デフォルトではfalseで初期化される
boolean[][] exist = new boolean[4][14];
int n = scan.nextInt();
for (int i = 0; i < n; i++) {
String s = scan.next();
int num = scan.nextInt();
exist[suit.indexOf(s)][num] = true;
}
for (int i = 0; i < 4; i++) {
for (int j = 1; j <= 13; j++) {
if (!exist[i][j]) {
System.out.println(suit.charAt(i) + " " + j);
}
}
}
scan.close();
}
}
//「すべてのカード」を定義する。あるか否かで判断したいのでboolean型にする。
// 最初は全部ない状態にしておく
//「すべてのカード」配列のうち、持っているカード(要素)をtrueで記録する。
// 「すべてのカード」のうち持っていないカードを表示する。
- suit.indexOf(s):入力されたスート(S,H,C,D)の文字がsuit文字列の何番目にあるかを返す
例:'S'なら0、'H'なら1、'C'なら2、'D'なら3
exist[スートの位置][カードの数字] = trueでそのカードが存在することを記録 - 各カードについて「存在するかしないか」という2つの状態だけを管理すれば良い→boolean型の配列で管理
- charAt()→文字列からn番目の文字列を抜き出す
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int a = scanner.nextInt(); // 入力されるカードの枚数
int[][] array = new int[4][13]; // 4スーツ(S, H, C, D)、13枚のカードの存在管理
// カード情報を入力して、配列に反映
for (int i = 0; i < a; i++) {
String suit = scanner.next(); // スーツ(S, H, C, D)
int number = scanner.nextInt(); // カード番号(1〜13)
// スーツに対応するインデックスにカードを追加
int suitIndex = getSuitIndex(suit);
array[suitIndex][number - 1] = 1; // 配列の番号は0から始まるので、number-1
}
// カードが足りないものを出力
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 13; j++) {
if (array[i][j] == 0) { // カードがない場合
System.out.println(getSuitName(i) + " " + (j + 1)); // スーツと番号を出力
}
}
}
scanner.close(); // Scannerのリソースを閉じる
}
// スーツ名に対応するインデックスを返す
private static int getSuitIndex(String suit) {
switch (suit) {
case "S": return 0; // スペード
case "H": return 1; // ハート
case "C": return 2; // クラブ
case "D": return 3; // ダイヤ
default: throw new IllegalArgumentException("Invalid suit: " + suit);
}
}
// インデックスに対応するスーツ名を返す
private static String getSuitName(int suitIndex) {
switch (suitIndex) {
case 0: return "S";
case 1: return "H";
case 2: return "C";
case 3: return "D";
default: throw new IllegalArgumentException("Invalid suit index: " + suitIndex);
}
}
}
問3.公舎の入居者数(難)
-
A大学は1フロア10部屋、3階建ての公舎4棟を管理しています。公舎の入居・退去の情報を読み込み、各部屋の入居者数を出力するプログラムを作成して下さい。
-
n件の情報が与えられます。各情報では、4つの整数b, f, r, vが与えられます。これは、b棟f階のr番目の部屋にv人が追加で入居したことを示します。vが負の値の場合、-v人退去したことを示します。
最初、全ての部屋には誰も入居していないものとします。 -
Input
最初の行に情報の数 n が与えられます。
続いて n 件の情報が与えられます。各情報には4つの整数 b, f, r, v が空白区切りで1行に与えられます。 -
Output
4棟について入居者数を出力して下さい。各棟について、1階、2階、3階の順に入居者数を出力します。各階については、1番目、2番目、・・・、10番目の部屋の入居者数を順番に出力します。入居者数の前には1つの空白を出力して下さい。また、各棟の間には####################(20個の#)で区切って下さい。
PHP
<?php
$num = trim(fgets(STDIN));
$zero = array_fill(0,10,0);
$arr = array($zero,$zero,$zero);
$entire = array($arr,$arr,$arr,$arr);
for($i=0;$i<$num;$i++){
fscanf(STDIN,"%d %d %d %d",$w,$f,$n,$v);
$entire[$w-1][$f-1][$n-1]+=$v;
}
//$entire の各 寮(棟) を $arr として取得。
foreach ($entire as $i => $arr) {
//$arr の各階($floor)をループで処理。
foreach ($arr as $floor) {
printf(" %s\n", implode(" ", $floor));
}
if ($i !== 3) {
echo str_repeat("#", 20) . "\n";
}
}
//ワンフロアの構造を定義
//全フロアの構造を定義
//全棟の構造を定義(全体としては4次元配列)
//入力からデータを書き換える
?>
- データの入力を受け取る
→データ構造(4棟 × 3階 × 10部屋)を作成
→入力される入居・退去データを配列に反映
→結果を出力。格棟の各階を表示させる。 - $entire配列の実態
$entire = [
[ // 第1棟($i=0)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // 1階
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // 2階
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0], // 3階
],
[ // 第2棟($i=1)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
],
[ // 第3棟($i=2)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
],
[ // 第4棟($i=3)
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
]
];
- foreachをネストで使う
//基本のforeachの使いかた...配列の 各要素を $value に代入してループ処理
$array = [1, 2, 3];
foreach ($array as $value) {
echo $value . "\n";
}
//foreachのネストでの使いかた(例①二次元配列)...外側から配列を変数に格納→要素を変数に格納
$data = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
foreach ($data as $row) { // 各配列を $row に格納(1行ずつ取得)
foreach ($row as $num) { // 各値を $num に格納(1つずつ取得)
echo $num . " ";
}
echo "\n"; // 1行ごとに改行
}
//出力
1 2 3
4 5 6
7 8 9
Java
import java.util.Scanner;
public class Main {
public static void main(String[] args){
Scanner scan = new Scanner(System.in);
// 配列のサイズ(棟、階、部屋)を定義
int num[][][] = new int[4][3][10];
// 入力データの数
int n = scan.nextInt();
for (int i = 0; i < n; i++) {
int b = scan.nextInt();
f = scan.nextInt();
r = scan.nextInt();
v = scan.nextInt();
num[b - 1][f - 1][r - 1] += v;
}
for (int i = 0; i < 4; i++) { // 4棟
for (int j = 0; j < 3; j++) { // 3階
for (int k = 0; k < 10; k++) { // 10部屋
System.out.print(" " + num[i][j][k]);
}
System.out.println();
}
if (i < 3) { // 最後の棟には区切りを入れない
for (int j = 0; j < 20; j++) System.out.print('#');
System.out.println();
}
}
scan.close();
}
}
-
3次元の配列でデータを管理→出力も3重ループ
-
入力が1ベース(棟、階、部屋が1から始まる)→配列は0ベースなら-1ひくことで帳尻合わせる
-
今回のように空白有の連続した入力→PHPではfscanfで受け取れたけどjavaだとひとつずつnextInt()等で受け取る。もしくは文字列ならsplit(区切り文字)←文字列配列が返る
問4.ベクトルと行列の積
n×mの行列Aと、m×1の列ベクトルbを読み込み、Aとbの積を出力するプログラムを作成してください。
PHP
<?php
fscanf(STDIN, "%d %d", $n, $m);
// 行列 A の読み込み
$A = array();
for ($i = 0; $i < $n; $i++) {
$A[$i] = explode(" ", trim(fgets(STDIN)));
}
// ベクトル b の読み込み
$b = array();
for ($i = 0; $i < $m; $i++) {
$b[$i] = trim(fgets(STDIN));
}
// 行列とベクトルの積を計算
for ($i = 0; $i < $n; $i++) {// Aの各行ごとに処理
$sum = 0;// 各行の計算結果を保持
for ($j = 0; $j < $m; $j++) {
$sum += (int)$A[$i][$j] * (int)$b[$j];
}
echo $sum . PHP_EOL;
}
?>
- explode() が返す配列の要素は文字列なので注意
- 行列Aとベクトルbの積は、行ごとに内積を計算する
→1行の各要素と、ベクトルの要素を掛け算して合計を求める処理が必要になる。
例
n = 3, m = 4 // A の行数は 3、列数は 4(b の要素数も 4)
A = [ //外側$iのループで一行ずつ処理
[1, 2, 0, 1],
[0, 3, 0, 1],
[4, 1, 1, 0]
]
b = [1, 2, 3, 0]//内側$jのループで同じインデックス同士の要素を掛け算
Java
import java.util.*;
public class Main{
public static void main(String[]args){
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int [][] A = new int [n][m];
for(int i = 0;i<n;i++){
for(int j =0;j<m;j++){
A[i][j]=sc.nextInt();
}
}
int [] b = new int [m];
for(int i =0;i<m;i++){
b[i] = sc.nextInt();
}
for(int i =0;i<n;i++){
int sum =0;
for(int j =0;j<m;j++){
sum += A[i][j]*b[j];
}
System.out.println(sum);
}
sc.close();
}
}