AOJ PG入門 #7

## 問1.成績
- 複数の学生のテストの点数を読み込みます。

- テストの点数は、中間試験の点数 m、期末試験の点数 f、再試験の点数 r で構成されています。

- 中間試験と期末試験は 50 点満点（m, f ≤ 50）、再試験は 100 点満点 （r ≤ 100）です。

- 試験を受けていない場合は点数を -1 とします。

- 以下の手順で成績が付けられます：

中間試験、期末試験のいずれかを欠席した場合成績は F。
中間試験と期末試験の合計点数が 80 以上ならば成績は A 。
中間試験と期末試験の合計点数が 65 以上 80 未満ならば成績は B。
中間試験と期末試験の合計点数が 50 以上 65 未満ならば成績は C。
中間試験と期末試験の合計点数が 30 以上 50 未満ならば成績は D。 ただし、再試験の点数が 50 以上ならば成績は C。
中間試験と期末試験の合計点数が 30 未満ならば成績は F。
- Input
複数のデータセットが入力として与えられます。各データセットでは、空白で区切られた m、f、r が 1 行に与えられます。
m、f、r がすべて -1 のとき入力の終わりとします。
データセットの数（生徒の数）は 50 を超えません。

- Output
各データセットについて、成績（A、B、C、D、または F）を１行に出力して下さい。
### PHP
```AOJ.php
<?php
while (true) {
    $line = trim(fgets(STDIN));
    if ($line === false || $line === "") {
        break;
    }
    
    list($m, $f, $r) = array_map('intval', explode(" ", $line));
    
    if ($m == -1 && $f == -1 && $r == -1) {
        break;
    }
    
    if ($m == -1 || $f == -1) {
        echo "F\n";
        continue;
    }

    $sum = $m + $f;

    if ($sum >= 80) {
        echo "A\n";
    } elseif ($sum >= 65) {
        echo "B\n";
    } elseif ($sum >= 50) {
        echo "C\n";
    } elseif ($sum >= 30) {
        if ($r >= 50) {
            echo "C\n";
        } else {
            echo "D\n";
        }
    } else {
        echo "F\n";
    }
}
?>
```
- switch文では今回のような範囲指定の条件分岐（○○>=$$××<=）ができないのでif-elseで処理
- continue 文は、ループの現在の繰り返し処理をスキップし、次の繰り返しへ移る→今回はどちらか不参加でF判定が出た場合、現在のループ＝while内の後継処理を丸々スキップ
- 忘れがちだから再掲...fgetsは一つの文字列ではなく一行の文字列を受け取る

### Java
```AOJ.java
import java.util.Scanner;
public class Main{
    public static void main(String[]args){
        Scanner sc = new Scanner(System.in);
        while(true){
            int m = sc.nextInt();
            int f = sc.nextInt();
            int r = sc.nextInt();
            if(m==-1&&f==-1&&r==-1){
                break;
            }
            if(m==-1||f==-1){
                System.out.println("F");
                continue;
            }
            int sum = m+f;
            if(sum>=80){
                System.out.println("A");
            }else if(sum>=65&&sum<80){
                System.out.println("B");
            }else if(sum>=50&&sum<65){
                System.out.println("C");
            }else if(sum>=30&&sum<50){
                System.out.println(r>=50?"C":"D");
            }else{
                System.out.println("F");
            }
        }
        sc.close();
    }
}
```
- elseには条件つけない。{}だけ！

## 問2.組み合わせの数
- 1 から n までの数の中から、重複無しで３つの数を選びそれらの合計が x となる組み合わせの数を求めるプログラムを作成して下さい。
- 例えば、1 から 5 までの数から３つを選んでそれらの合計が 9 となる組み合わせは、
1 + 3 + 5 = 9
2 + 3 + 4 = 9
の２通りがあります。
- Input
複数のデータセットが入力として与えられます。各データセットでは、空白で区切られた n、x が 1 行に与えられます。n、x がともに 0 のとき入力の終わりとします。
- Output
各データセットについて、組み合わせの数を１行に出力して下さい。
### PHP
```AOJ.php
function countCombinationsOptimized($n, $x) {
    $count = 0;
    
    // 最初の2つの数のみをループで選び、3つ目の数は計算で求める
    for ($i = 1; $i <= $n - 2; $i++) {
        for ($j = $i + 1; $j <= $n - 1; $j++) {
            // 3つ目の数を計算
            $k = $x - ($i + $j);
            
            // 3つ目の数が条件を満たすかチェック
            if ($k > $j && $k <= $n) {
                $count++;
            }
        }
    }
    
    return $count;
}
```
- 重複なしの組み合わせ→大小関係を制約することで重複を排除
→一方の数が必ずもう一方の数より小さく（または大きく）なるように範囲設定
- 例：n=5の場合→i は 1, 2, 3 まで動きjはi+1～n-1を動く
i = 1 のとき、j は 2, 3, 4 を動く
i = 2 のとき、j は 3, 4 を動く
i = 3 のとき、j は 4 を動く
→それぞれで3つ目の数をつくり、それが条件を満たしているならばカウントする
- k は i と j より大きい必要がある（i < j < k の条件を守るため）

### 別解
```other.php
$input = STDIN;

while (true) {
    list($n, $x) = explode(' ', trim(fgets($input)));

    if ($n == 0 && $x == 0) break;

    $min_a = max(intval($x / 3) + 1, 3);
    $max_a = min($n, $x - 3);
    $cnt = 0;

    for ($a = $min_a; $a <= $max_a; $a++) {
        $rest = $x - $a;
        $min_b = max(intval($rest / 2) + 1, 2);
        $max_b = min($rest - 1, $a - 1);

        $cnt += ($max_b - $min_b + 1);
    }

    print "{$cnt}\n";
}

```
### Java
```AOJ.java
import java.util.Scanner;
public class Main{
    public static void main(String[]args){
        Scanner sc = new Scanner(System.in);
        while(true){
        int n = sc.nextInt();
        int x = sc.nextInt();
        if(n==0&&x==0)break;
        int cnt = 0;
        for(int i = 1;i<=n-2;i++){
            for(int j=i+1;j<=n-1;j++){
                int k = x-(i+j);
                if(k<=n&&k>j){
                    cnt+=1;
                }
            }
        }
        System.out.println(cnt);
        }
        sc.close();
    }
}
```

### 問3.表計算プログラム
表の行数rと列数c、r × c の要素を持つ表を読み込んで、各行と列の合計を挿入した新しい表を出力するプログラムを作成して下さい。
- Input
最初の行にrとcが空白区切りで与えられます。続くr行にそれぞれc個の整数が空白区切りで与えられます。

- Output
(r+1) × (c+1) の新しい表を出力して下さい。各行の隣り合う整数は１つの空白で区切って下さい。各行の最後の列としてその行の合計値を、各列の最後の行としてその列の合計値を、最後の行・列に表全体の合計値を挿入して下さい。
```AOJ.php
<?php

list($r, $c) = explode(" ", trim(fgets(STDIN)));


$array1 = array_fill(0, $r + 1, array_fill(0, $c + 1, 0));


for ($i = 0; $i < $r; $i++) {
    // 1行分のデータを空白で分割し、整数値に変換
    $row = array_map("intval", explode(" ", trim(fgets(STDIN))));
    for ($j = 0; $j < $c; $j++) {
        $array1[$i][$j] = $row[$j];      // 受け取ったデータを、$array1 という表の i 行目 j 列目に入れる
        $array1[$i][$c] += $row[$j];     // 各行の合計を計算（行の最後の列に格納）
        $array1[$r][$j] += $row[$j];     // 各列の合計を計算（列の最後の行に格納）
        $array1[$r][$c] += $row[$j];     // 全体の合計を計算（右下のマスに格納）
    }
}


for ($i = 0; $i <= $r; $i++) {
    echo implode(" ", $array1[$i]) . "\n";
}
//行数 $r と 列数 $c を取得
//サイズ (r+1) × (c+1) の2次元配列 $array1 を作成し、すべて 0 で初期化(データ構造作成)
//「一行分データ」配列に一行分入力を入れる
//「一行分データ」配列のj番目の要素を二次元配列にセット

?>

```
- array_fillによる二次元配列の作り方
```
$array1 = array_fill(0, $r + 1, array_fill(0, $c + 1, 0));
```
この結果は
```
[
    [0, 0, 0, 0, 0],  // 1行目
    [0, 0, 0, 0, 0],  // 2行目
    [0, 0, 0, 0, 0],  // 3行目
    [0, 0, 0, 0, 0]   // 4行目（合計値用）
]

```

### 別解
```other.php
<?php
$input = STDIN;

list($r, $c) = explode(' ', trim(fgets($input)));

$s = array_fill(0, $c, 0);

for ($i = 0; $i < $r; $i++) {
    $line = trim(fgets($input));
    
    print $line;

    $line = explode(' ', $line);
    print ' ' . array_sum($line) . "\n";
    //＄line の各要素を ＄s に加算する
    foreach ($line as $key => $value) {
        $s[$key] += $value;
    }
}

print implode(' ', $s) . ' ' . array_sum($s) . "\n";
//行数 $r と 列数 $c を取得
//列ごとの合計を格納する1次元配列 $s を作成し、すべて 0 で初期化
//各行を入力し、そのまま出力
//行の合計値を計算し、右端に追加して出力
//列の合計をSのインデックスごとに格納
```
- ＄s は forループの外側で宣言されているのでプログラム全体で ＄s を参照・更新できる

### Java
```APJ.java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        // 行数と列数の入力を受け取る
        int r = sc.nextInt();
        int c = sc.nextInt();
        
        // (r+1)×(c+1)の2次元配列を作成
        int[][] array = new int[r + 1][c + 1];
        
        // 配列に値を入力
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                array[i][j] = sc.nextInt();
            }
        }
        
        // 行ごとの合計値を計算して格納
        for (int i = 0; i < r; i++) {
            int sum = 0;
            for (int j = 0; j < c; j++) {
                sum += array[i][j];  // 行ごとの合計を計算
            }
            array[i][c] = sum;  // 最後の列に合計値を格納
        }
        
        // 列ごとの合計値を計算して格納
        for (int j = 0; j < c; j++) {
            int sum = 0;
            for (int i = 0; i < r; i++) {
                sum += array[i][j];  // 列ごとの合計を計算
            }
            array[r][j] = sum;  // 最後の行に合計値を格納
        }
        
        // 全体の合計を計算（右下のセル）
        int totalSum = 0;
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                totalSum += array[i][j];
            }
        }
        array[r][c] = totalSum;
        
        // 配列の出力
        for (int i = 0; i <= r; i++) {
            for (int j = 0; j <= c; j++) {
                System.out.print(array[i][j]);
                if (j < c) {
                    System.out.print(" ");
                }
            }
            System.out.println();
        }
        
        sc.close();
    }
}
```
### 別解
```Other.java
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);
        
        // 行数と列数の入力を受け取る
        int r = sc.nextInt();
        int c = sc.nextInt();
        
        // (r+1)×(c+1)の2次元配列を作成（合計用に1行1列多く確保）
        int[][] array = new int[r + 1][c + 1];
        
        // 配列に値を入力し、同時に合計を計算
        for (int i = 0; i < r; i++) {
            for (int j = 0; j < c; j++) {
                array[i][j] = sc.nextInt();
                // 行の合計を更新（最後の列）
                array[i][c] += array[i][j];
                // 列の合計を更新（最後の行）
                array[r][j] += array[i][j];
                // 全体の合計を更新（右下）
                array[r][c] += array[i][j];
            }
        }
        
        // 配列の出力
        for (int i = 0; i <= r; i++) {
            for (int j = 0; j <= c; j++) {
                System.out.print(array[i][j]);
                if (j < c) {
                    System.out.print(" ");
                }
            }
            System.out.println();
        }
        
        sc.close();
    }
}
```
## 問4.行列の積
n×mの行列Aとm×lの行列Bを入力し、それらの積である n×lの行列Cを出力するプログラムを作成してください。
- input
１行目にn,m,lが空白区切りで与えられます。続く行にn*mの行列Aとm*lの行列Bが与えられます。
- output
n*lの行列Cの要素cijを出力してください。各行の隣り合う要素を１つの空白で区切ってください。
### PHP
```AOJ.php
<?php
//n,m,lの数字を入力してもらう
fscanf(STDIN,"%d %d %d",$n,$m,$l);
//行列Ａをつくる。二次元配列[i][j](i=0~n,j=0~m)に入力値をいれる
$arrA = array_fill(0,$n,array_fill(0,$m,0));
for($i=0;$i<$n;$i++){
    $row = array_map("intval",explode(" ",trim(fgets(STDIN))));
    for($j=0;$j<$m;$j++){
        $arrA[$i][$j] = $row[$j];
    }
}
//行列Ｂをつくる。二次元配列[i][j](i=0~m,j=0~l)に入力値をいれる
$arrB = array_fill(0,$m,array_fill(0,$l,0));
for($i=0;$i<$m;$i++){
    $row = array_map("intval",explode(" ",trim(fgets(STDIN))));
    for($j=0;$j<$l;$j++){
        $arrB[$i][$j] = $row[$j];
    }
}
//行列Ｃをつくる。
$arrC = array_fill(0,$n,array_fill(0,$l,0));
for ($i = 0; $i < $n; $i++) {
    for ($j = 0; $j < $l; $j++) {
        $sum = 0; // 各要素の計算結果を格納
        for ($k = 0; $k < $m; $k++) {
            $sum += $arrA[$i][$k] * $arrB[$k][$j];
        }
        $arrC[$i][$j] = $sum;
    }
}

// 行列Cの出力
for ($i = 0; $i < $n; $i++) {
    echo implode(" ", $arrC[$i])."\n";
}
?>
```
- 行列𝐴のi行目の要素と、行列Bのj列目の要素を掛ける。その総和を𝐶[𝑖][𝑗]に入れる。
- 3重ループの構造

外側 (＄i) … 行列 C の行（行列 A の行）
中側 (＄j) … 行列 C の列（行列 B の列）
内側 (＄k) … 掛け算と合計を計算
### 別解
```other.php
<?php
list($n, $m, $l) = array_map("intval", explode(" ", trim(fgets(STDIN))));

$a = [];
for ($i=0; $i<$n; $i++) {
	$a_line = array_map("intval", explode(" ", trim(fgets(STDIN))));
	$a[$i] = $a_line;
}

$b = [];
for ($j=0; $j<$m; $j++) {
	$b_line = array_map("intval", explode(" ", trim(fgets(STDIN))));
	$b[$j] = $b_line;
}

$c = [];
for ($k=0; $k<$n; $k++) {
	$c[$k] = array_fill(0, $l, 0);
}

foreach ($a as $i => $a_nums) {
    foreach ($b as $j => $b_nums) {
        foreach ($b_nums as $k => $n) {
            $c[$i][$k] += $n * $a_nums[$j];
        }
    }
    echo implode(' ', $c[$i]), PHP_EOL;
}

```
- ループの動き
A[i][j] の値を取得
B[j][k] の値を取得
A[i][j]×B[j][k] を計算
C[i][k] に加算
### Java
```AOJ.java
import java.util.Scanner;

public class Main {
    public static void main(String[] args){
        Scanner sc=new Scanner(System.in);
        int n=sc.nextInt();
        int m=sc.nextInt();
        int l=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][l];
        for(int i=0;i<m;i++){
            for(int j=0;j<l;j++){
                b[i][j]=sc.nextInt();
            }
        }
        long[][] c=new long[n][l];
        for(int i=0;i<n;i++){
            for(int j=0;j<l;j++){
                for(int k=0;k<m;k++){
                    c[i][j]+=a[i][k]*b[k][j];
                }
            }
        }
        for(int i=0;i<n;i++){
            for(int j=0;j<l;j++){
                if(j>0)System.out.print(" ");
                System.out.print(c[i][j]);
            }
            System.out.println();
        }
        sc.close();
    }
}


```