Ex09

From Prog0

Jump to: navigation, search

演習第9回

Contents

演習問題

主な内容

  • 2次元配列
  • マクロ

以下の問題を解いて期限内に解答を提出してください。

出席確認

  • 演習時間中に出席確認をLMS上の「演習出欠」で行ってください
    • 出席確認用のパスワードは演習時間のどこかのタイミングで提示されます

予備知識

今回の演習では以下のように入力の途中に改行が入った例が含まれている。

1 2 3
4 5 6

scanfは改行も空白と同様に区切り文字として扱うので、このような場合でもscanfの書き方は変わらず「scanf("%d", &a)」のように書けば良い。

ハンドアウトの例題

問題に取り掛かる前にハンドアウトの例題 lec09-4.c を以下のようにコピーし、2次元配列の初期化の方法を確認した上でコンパイル・実行を試すこと。

% cd ~/Prog0/Ex09
% cp /home/course/prog0/public_html/2023/lec/source/lec09-4.c .

A問題

A-1 行列とマクロ

ファイル名: ex09a1.c

以下はキーボードから入力する2つの3 x 2 行列の和を計算して表示するプログラムである。実行例を参照に、下線部および指示された箇所を補い、プログラムを完成させなさい。なお、行列の要素を格納する配列の行数と列数はそれぞれROW、COLUMNというマクロで定義するものとし、配列の宣言やfor文の条件式に3や2などの定数値は書かないこととする。

#include <stdio.h>
________________
________________

int main()
{
  int i, j;
  int mat1[_____][_____],mat2[_____][_____],mat3[_____][_____];
  
  printf("%d x %d 行列[mat1]の要素を入力してください:\n", ROW, COLUMN);
  for(________________)
    for(________________)
      scanf("%d", &mat1[i][j]);
      
  printf("%d x %d 行列[mat2]の要素を入力してください:\n", ROW, COLUMN);
  for(________________)
    for(________________)
      scanf("%d", &mat2[i][j]);
      
  /* この部分を補う */
  
  printf("[mat1]と[mat2]の和は:\n");
  for(________________){
    for(________________){
      printf("%d ", mat3[i][j]);
    }
    printf("\n");
  }

  return 0;
}

[実行例]

%./a.out
3 x 2 行列[mat1]の要素を入力してください:
1 2 
3 4
5 6
3 x 2 行列[mat2]の要素を入力してください:
1 2
3 4
5 6
[mat1]と[mat2]の和は:
2 4 
6 8 
10 12 
%

A-2 行列の主対角線にある要素値

ファイル名: ex09a2.c

次のプログラムの誤りを修正し、実行例のように行列(2次元配列)の主対角線にある要素値を表示するプログラムを作成しなさい。

#include <stdio.h>

int main()
{
  int i, j, mat[][3]={1 2 3 4 5 6 7 8 9};
  
  for (i=0; i<3; i++){
    for (j=0; j<=3; j++){
      printf("%d ", mat[j][i]);
    }
    printf("\n");  
  }
  printf("行列の主対角線にある要素値\n");
  for (i=0; i<3; i++){
    printf("%d ", mat[i]);
  }
  printf("\n");

  return 0;
}

[実行例]

%./a.out
1 2 3
4 5 6
7 8 9
行列の主対角線にある要素値
1 5 9
%

A-3 行列サイズの変換

ファイル名: ex09a3.c

実行例のように5 x 3行列を3 x 5行列に移し替えたい。
つまり、5x 3行列の15個の要素値を一列に並んだ数列として順番に読み出し、新たに3 x 5行列の15個の要素に順番に入れることで実現できる (ハンドアウトLec09-13の図も参考にせよ)
以下の要件を満たすプログラムを作成しなさい。

  • 2つの2次元配列 inmat[5][3] と outmat[3][5] を使用すること
  • 行列の要素値は2桁以下の自然数とする
  • 入出力の処理には二重ループを使用すること

実行例

%./a.out
5 x 3行列を整数値で入力してください
1 2 3 
4 5 6
7 8 9
10 11 12
13 14 15 

変換した3 x 5行列の表示:
 1  2  3  4  5
 6  7  8  9 10
11 12 13 14 15

B問題

B-1 行列要素の抜き出し

ファイル名: ex09b1.c

キーボードからの入力に応じて行列の行要素または列要素を表示するプログラムを、実行例を参考にして作成しなさい。行列は 4 x 3 の行列とし、2次元配列の宣言時に以下の要素値で初期化するものとする。

 1  2  3 
 4  5  6 
 7  8  9 
10 11 12

最初に入力された数値が「0」の場合はその後指定された行要素を、「1」の場合は列要素を同様に表示する。その他の数値が入力された場合は行列全体を表示するものとする。また、指定した行番号、または列番号が範囲外の場合は、下記の実行例に示すエラーを出力するようにすること。

[実行例]

%./a.out
行要素と列要素のどちらを表示したいか選択して下さい
(行要素:0, 列要素:1)  0
表示したい行番号を入力して下さい :2
 7 8 9
%./a.out
行要素と列要素のどちらを表示したいか選択して下さい
(行要素:0, 列要素:1)  1
表示したい列番号を入力して下さい :0
 1
 4
 7
10
%./a.out
行要素と列要素のどちらを表示したいか選択して下さい
(行要素:0, 列要素:1)  -1
 1  2  3
 4  5  6
 7  8  9
10 11 12
%./a.out
行要素と列要素のどちらを表示したいか選択して下さい
(行要素:0, 列要素:1)  0
表示したい行番号を入力して下さい :4
 行の範囲外です
%./a.out
行要素と列要素のどちらを表示したいか選択して下さい
(行要素:0, 列要素:1)  1
表示したい列番号を入力して下さい :-2
 列の範囲外です
%

B-2 2次元配列の一部の利用と行列の積

ファイル名: ex09b2.c

n x m行列の行列Aと、m×n行列の行列Bの積を計算して、その結果を表示するプログラムを作成しなさい。
ただし、プログラム中ではSIZE x SIZE (SIZEはマクロで定義し、値は5とする)の2次元配列を用意し、 行列Aはそのn行m列目まで、行列Bはm行n列目までを使って各要素を格納し、処理を行うようにすること。 行列サイズ n, m と要素値はキーボードから入力する。

[実行例]

%./a.out
行列の大きさNとMを入力して下さい(最大5 x 5) :2 3
2x 3の行列Aの要素を整数値で入力してください
1 2 3
4 5 6
3x 2の行列Bの要素を整数値で入力してください
1 2
3 4
5 6
行列A*行列Bの結果
22  28
49  64

%./a.out
行列の大きさNとMを入力して下さい(最大5 x 5) :3 2
3x 2の行列Aの要素を整数値で入力してください
1 2
3 4
5 6
2x 3の行列Bの要素を整数値で入力してください
1 2 3
4 5 6
行列A*行列Bの結果
 9 12 15
19 26 33
29 40 51

Extra問題

E-1 〇×三目並べゲーム

ファイル名: ex09e1.c

以下のプログラムは3x3のマス上で遊ぶ〇×の三目並べゲームであるが、不完全な部分がある。正しく動作するように改良しなさい。

プログラムの仕様:

  • マスとなる配列の要素はあらかじめ「0」に初期化しておく。
  • 何も置かれていないところの要素は「0」、〇が置かれたところは「1」、×が置かれたところは「-1」になるとする。
  • マスの表示は要素値によって該当する文字(〇か×)を表示する(盤が見にくくなるが座標も一緒に表示する)。
  • 座標の入力はflagが「0」のとき〇の番、「1」のとき×の番になるようにして、それぞれの番が終ったときに相手の番になるように、flagの値を反転させる。


#include <stdio.h>
#define NUM 3

int main()
{
  int map[NUM][NUM];
  int i, j, m, n;
  int flag = 0;

  /* 配列の初期化 */
  for (i=0; i<NUM; i++)
    for (j=0; j<NUM; j++)
      map[i][j] = 0;

  while (1) {
  /* 盤の表示 */
    for (i=0; i<NUM; i++) {
      for (j=0; j<NUM; j++) {
        if (map[i][j]==1) printf("(%d %d) 〇 ", i, j);
        else if (map[i][j]==-1) printf("(%d %d) × ", i, j);
        else printf("(%d %d)   ", i, j);
      }
      printf("\n");
    }

    /* 座標の入力 */
    if (flag==0) {
      printf("〇の番です\n座標を行・列の順で入力してください:");
      scanf("%d%d", &m, &n);
      map[m][n] = 1;
      flag = 1;
    } else {
      printf("×の番です\n座標を行・列の順で入力してください:");
      scanf("%d%d", &m, &n);
      map[m][n] = -1;
      flag = 0;
    }
  }
  
  return 0;
}

改良すべき問題点:

  • 座標入力の際に、盤面上(配列の範囲内)であるかチェックしていない。
  • 既に〇×が置かれていても再度置けてしまう。
  • 終了判定(勝ち、負け、引き分け)がない。

なお、終了判定がない状態でプログラムを実行し、それを強制的に停止させたい場合は「Ctrl + c」を押すこと。

終了判定のヒント:

  • 〇の勝ち:縦、横、斜め、それぞれの合計が何処か1箇所でもNUMになったとき
  • ×の勝ち:縦、横、斜め、それぞれの合計が何処か1箇所でも-NUMになったとき
  • 引き分け:配列の中に「0」の要素がなくなったとき

[実行例]

%./a.out
(0 0)   (0 1)   (0 2)   
(1 0)   (1 1)   (1 2)   
(2 0)   (2 1)   (2 2)   
〇の番です
座標を行・列の順で入力してください:0 0
(0 0) 〇 (0 1)   (0 2)   
(1 0)   (1 1)   (1 2)   
(2 0)   (2 1)   (2 2)   
×の番です
座標を行・列の順で入力してください:0 1
(0 0) 〇 (0 1) × (0 2)   
(1 0)   (1 1)   (1 2)   
(2 0)   (2 1)   (2 2)   
〇の番です
座標を行・列の順で入力してください:1 1
(0 0) 〇 (0 1) × (0 2)   
(1 0)   (1 1) 〇 (1 2)   
(2 0)   (2 1)   (2 2)   
×の番です
座標を行・列の順で入力してください:1 1
そこには打てません!
(0 0) 〇 (0 1) × (0 2)   
(1 0)   (1 1) 〇 (1 2)   
(2 0)   (2 1)   (2 2)   
×の番です
座標を行・列の順で入力してください:0 2
(0 0) 〇(0 1) × (0 2) × 
(1 0)   (1 1) 〇 (1 2)   
(2 0)   (2 1)   (2 2)   
〇の番です
座標を行・列の順で入力してください:3 2
盤の範囲外です。
(0 0) 〇 (0 1) × (0 2) × 
(1 0)   (1 1) 〇 (1 2)   
(2 0)   (2 1)   (2 2) 
〇の番です
座標を行・列の順で入力してください:2 2
(0 0) 〇 (0 1) × (0 2) × 
(1 0)   (1 1) 〇 (1 2)   
(2 0)   (2 1)   (2 2) 〇 
〇の勝ち!!
%

課題提出上の注意事項

解答ファイルはmenuコマンドを使って提出してください。以下のようにmenuコマンドを実行し、表示されるメッセージに沿って操作すること。

% ~prog0/bin/menu

menuコマンドは、解答ファイルが ~/Prog0/Ex## のディレクトリに指定されたファイル名で置かれているものとして処理します。正常に提出された場合は ○ が、何らかのエラーが生じた場合は × が表示されます。

解答の提出期間は以下のとおりです。

問題提出受付開始提出〆切
A問題 演習日の6日前の午後9時演習終了時刻
B, Extra問題 演習日の6日前の午後9時演習日の6日後の午後9時

提出は〆切前であれば何度でもやり直すことができます。再提出すると、前に提出したファイルは新しい内容で上書きされます。

Personal tools