0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

AOJ PG入門 #6

Last updated at Posted at 2025-02-08

問1.与えられた数列を逆順に出力するプログラムを作成して下さい。

PHP

AOJ.php
<?php

    $n= intval(trim(fgets(STDIN)));
    $num = explode(" ",trim(fgets(STDIN)));
    
        $num = array_reverse($num);
        echo  implode(" ",$num)."\n";   
?>

Java

AOJ.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

AOJ.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)が出力されてしまう

別解

Other.php
<?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

AOJ.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.java
//removeAll()を使う場合
allCards.removeAll(existingCards);
for(String card : allCards) {
    System.out.println(card);
}
iterator.java
//イテレーターを使う場合
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);
}

別解

other.java
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番目の文字列を抜き出す
other2.java
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

AOJ.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配列の実態
arr.php
$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をネストで使う
ex.php
//基本の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

AOJ.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

AOJ.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行の各要素と、ベクトルの要素を掛け算して合計を求める処理が必要になる。
ex.php
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

AOJ.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();
    }
}
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?