問1.サイコロ①
サイコロの各面には図のとおりに 1 から 6 のラベルが割りあてられています。
入力としてサイコロの各面のラベルに対応する整数と、転がす命令の列が与えられるので、サイコロの上面の整数を出力してください。
- input
1行目に各面の整数が、図に示すラベルの順番に空白区切りで与えられます。
2行目に命令を表す1つの文字列が与えられます。命令はそれぞれ図に示す4方向を表す文字 E、N、S、W を含む文字列です。 - output
すべての命令を実行した後のサイコロの上面の数を1行に出力してください。
Java
import java.util.Scanner;
// サイコロ設計クラスと実行クラスを一つにまとめる
public class Main {
// サイコロ設計クラス
static class Dice {
int top;
int bottom;
int front;
int back;
int left;
int right;
// コンストラクタで面を設定
public Dice(int top, int front, int right, int left, int back, int bottom) {
this.top = top;
this.bottom = bottom;
this.front = front;
this.back = back;
this.left = left;
this.right = right;
}
// 東に転がす→left>>top, top>>right, right>>bottom, bottom>>left
public void rollEast() {
int temp = top; // 元々上にあった面
top = left; // 空いたtopにleftを移動
left = bottom; // 空いたleftにbottomを移動
bottom = right; // 空いたbottomにrightを移動
right = temp; // rightに元々上にあった面を移動
}
// 西に転がす→top>>left, left>>bottom, bottom>>right, right>>top
public void rollWest() {
int temp = top;
top = right;
right = bottom;
bottom = left;
left = temp;
}
// 北に転がす→front>>top, bottom>>front, back>>bottom, 元top>>back
public void rollNorth() {
int temp = top;
top = front;
front = bottom;
bottom = back;
back = temp;
}
// 南に転がす→back>>top, bottom>>back, front>>bottom, 元top>>front
public void rollSouth() {
int temp = top;
top = back;
back = bottom;
bottom = front;
front = temp;
}
// 現在の上面の値を取得
public int getTop() {
return top;
}
}
// 実行メインクラス
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// 面のラベルを入力
int top = sc.nextInt();
int front = sc.nextInt();
int right = sc.nextInt();
int left = sc.nextInt();
int back = sc.nextInt();
int bottom = sc.nextInt();
// サイコロを作成
Dice d = new Dice(top, front, right, left, back, bottom);
// 操作命令を入力
String commands = sc.next(); // 1行目に命令を入力
// 入力された命令を順番に実行
for (char command : commands.toCharArray()) {
switch (command) {
case 'E': // 東
d.rollEast();
break;
case 'W': // 西
d.rollWest(); // 修正: rollWesr -> rollWest
break;
case 'N': // 北
d.rollNorth();
break;
case 'S': // 南
d.rollSouth();
break;
}
}
// サイコロの上面の値を出力
int ans = d.getTop();
System.out.println(ans);
sc.close();
}
}
- 今回AOJの仕様上MainクラスとDiceクラスを別ファイルに分けてジャッジすることができなかったのでDiceクラスとMainクラスを1つのファイルにまとめた
- mainメソッドはstaticなので、mainメソッド内から直接他のクラスを利用したい場合、そのクラスもstaticにする→これにより、MainクラスからDiceクラスを参照できるようになる
- つまり一つのファイル内で完結させるためには、Mainクラス内に静的クラスとしての他のクラス(例えば、Diceクラス)を作成し、mainメソッド内でそのクラスのインスタンスを作成して動作させればいい
- int tempは、一時的に値を保存するための変数。これを用意しないと「中身topは一度箱からだし、空いた箱topに箱leftにあった中身を代入」ではなく「箱topの中身topが消えて、箱leftの中身がtopの箱に入る」
別解
import java.util.Scanner;
import java.io.FileNotFoundException;
import java.io.File;
import java.util.Arrays;
class Dice {
public static final int NUM_FACES = 6; //サイコロの面の数(6面)を定数として定義
private int[] faces; //各面の値を格納する配列
//コンストラクタ
//inputFaces で与えられた配列をfacesにコピー
public Dice(int[] inputFaces) {
faces = Arrays.copyOf(inputFaces, NUM_FACES);
}
public void rollNorth() {
int tmp = faces[0];
faces[0] = faces[1];
faces[1] = faces[5];
faces[5] = faces[4];
faces[4] = tmp;
}
public void rollEast() {
int tmp = faces[0];
faces[0] = faces[3];
faces[3] = faces[5];
faces[5] = faces[2];
faces[2] = tmp;
}
public void rollWest() {
int tmp = faces[0];
faces[0] = faces[2];
faces[2] = faces[5];
faces[5] = faces[3];
faces[3] = tmp;
}
public void rollSouth() {
int tmp = faces[0];
faces[0] = faces[4];
faces[4] = faces[5];
faces[5] = faces[1];
faces[1] = tmp;
}
public int getTop() {
return faces[0];
}
}
public class Main {
// public static final boolean READ_FROM_FILE = true;
public static final boolean READ_FROM_FILE = false;
public static void main(String[] args) throws FileNotFoundException {
//Scanner 型の変数を用意
Scanner sc;
if (READ_FROM_FILE) {
//READ_FROM_FILE == true なら Scanner をファイルから読むように設定
//File クラスを使って "test0.txt" というファイルを開く
File file = new File("test0.txt");
//new Scanner(file) で ファイル入力用の Scanner を作成
sc = new Scanner(file);
} else {
//READ_FROM_FILE == false なら 標準入力(キーボード)から読む
//new Scanner(System.in) で キーボードからの入力用の Scanner を作成
sc = new Scanner(System.in);
}
int[] faces = new int[Dice.NUM_FACES];
for (int i = 0; i < Dice.NUM_FACES; i++) {
faces[i] = sc.nextInt();
}
Dice d = new Dice(faces);
String operations = sc.next();
for (int i = 0; i < operations.length(); i++) {
switch (operations.charAt(i)) {
case 'N':
d.rollNorth();
break;
case 'E':
d.rollEast();
break;
case 'W':
d.rollWest();
break;
case 'S':
d.rollSouth();
break;
default:
}
}
System.out.println(d.getTop());
}
}
- Arrays.copyOf():第二引数で指定された要素数になるよう配列をコピー
- 入力の受け取り→READ_FROM_FILEがtrueならファイルから読み込み、falseなら標準入力(キーボード)から読み込み
- 文字列Stringを一文字ずつ取り出して検査→ループでcharAt(i)
- 配列を使用→Javaの配列は固定サイズであることからメモリの確保が一度きり、サイズが変わらないため、Javaのガベージコレクション (GC) の負担が減る、キャッシュ効率が良く、アクセス速度が向上するなど使用メリットが多い。また、メソッドで使われている固定サイズ配列のインデックス操作もCPUのレジスタやキャッシュに最適化されやすい、ループを使わず、定数時間で完了する (O(1))などメリットがある
- こちらのコードはStringの使用を最低限に抑えている
→JavaのStringは不変オブジェクトであり、変更があるたびに新しいインスタンスを生成
→ メモリ使用量が増加
→ GC(ガベージコレクション)の負担が増える
といったデメリットがあるためメモリ効率が悪くなる可能性がある - 以上のことからやってる内容はほとんど同じだがこちらの別解のほうがメモリ消費量や処理速度が優れている
問2.サイコロ②
Dice I と同様の方法で、入力された整数の列からサイコロをつくります。
このサイコロを Dice I の方法で回転させた後の上面と前面の整数が質問として与えられるので、右側の面の整数を答えるプログラムを作成してください。
- input
1行目に各面の整数が、ラベルの順番に空白区切りで与えられます。
2行目に質問の数qが与えられます。続くq行に質問が与えられます。各質問では上面と前面の整数が空白区切りで1行に与えられます。 - output
各質問ごとに、サイコロの右側の面の整数を1行に出力してください。
import java.util.Scanner;
import java.io.FileNotFoundException;
import java.io.File;
import java.util.Arrays;
class Dice {
public static final int NUM_FACES = 6;
private int[] face;
// コンストラクタ
public Dice(int[] inputFaces) {
face = Arrays.copyOf(inputFaces, NUM_FACES);
}
// 右の面を取得する
public int getRight(int up, int front) {
int right = 0;
if ((up == face[3] && front == face[1]) || (up == face[1] && front == face[2]) ||
(up == face[2] && front == face[4]) || (up == face[4] && front == face[3])) {
right = face[0];
}
if ((up == face[2] && front == face[0]) || (up == face[0] && front == face[3]) ||
(up == face[3] && front == face[5]) || (up == face[5] && front == face[2])) {
right = face[1];
}
if ((up == face[5] && front == face[4]) || (up == face[4] && front == face[0]) ||
(up == face[0] && front == face[1]) || (up == face[1] && front == face[5])) {
right = face[2];
}
if ((up == face[1] && front == face[0]) || (up == face[0] && front == face[4]) ||
(up == face[4] && front == face[5]) || (up == face[5] && front == face[1])) {
right = face[3];
}
if ((up == face[2] && front == face[5]) || (up == face[5] && front == face[3]) ||
(up == face[3] && front == face[0]) || (up == face[0] && front == face[2])) {
right = face[4];
}
if ((up == face[2] && front == face[1]) || (up == face[1] && front == face[3]) ||
(up == face[3] && front == face[4]) || (up == face[4] && front == face[2])) {
right = face[5];
}
return right;
}
}
public class Main {
public static final boolean READ_FROM_FILE = false;
public static void main(String[] args) throws FileNotFoundException {
Scanner sc;
if (READ_FROM_FILE) {
File file = new File("test0.txt");
sc = new Scanner(file);
} else {
sc = new Scanner(System.in);
}
int[] faces = new int[Dice.NUM_FACES];
for (int i = 0; i < Dice.NUM_FACES; i++) {
faces[i] = sc.nextInt();
}
Dice d = new Dice(faces);
int q = sc.nextInt();
for (int i = 0; i < q; i++) {
int up = sc.nextInt();
int front = sc.nextInt();
int ans = d.getRight(up, front);
System.out.println(ans);
}
sc.close();
}
}
別解
public class Main{
public void run(java.io.InputStream in, java.io.PrintStream out){
java.util.Scanner sc = new java.util.Scanner(in);
int[] d = new int[6];
int i, q, a, b;
for(i = 0;i < 6;i++)d[i] = sc.nextInt();
for(q = sc.nextInt();q-- > 0;out.println(solve(d, a, b))){
a = sc.nextInt(); b = sc.nextInt();
}
sc.close();
}
private static int solve(int[] d, int a, int b){
int i, j;
for(i = 0;i < 6;i++){
for(j = 0;j < 4;j++){
if(d[0] == a && d[1] == b)return d[2];
rroll(d);// 右に回転(前面を変える)
}
if(d[0] == a && d[1] == b)return d[2];
if(i % 2 == 0)nroll(d);
else eroll(d);
}
return -1;
}
public static void main(String[] args){
(new Main()).run(System.in, System.out);
}
private static void nroll(int[] dice){ //(1265)
int tmp;
tmp = dice[0]; dice[0] = dice[1]; dice[1] = dice[5];
dice[5] = dice[4]; dice[4] = tmp;
return;
}
private static void eroll(int[] dice){ //(1463)
int tmp;
tmp = dice[0]; dice[0] = dice[3]; dice[3] = dice[5];
dice[5] = dice[2]; dice[2] = tmp;
return;
}
private static void rroll(int[] dice){ //(2354)
int tmp;
tmp = dice[1]; dice[1] = dice[2]; dice[2] = dice[4];
dice[4] = dice[3]; dice[3] = tmp;
return;
}
}
- iのループは上面を変更するためのもので、全6面を試す
- jのループでは前面を変更
- rroll(d)は現在の上面を維持したまま、サイコロを右回転させる(前面が変わる)。これにより、同じ上面で4つの異なる前面の組み合わせを試せる
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Scanner;
class Main {
public static void main(String[] args) throws IOException {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//BufferedReader br=new BufferedReader(new FileReader("sample.txt"));
String str=br.readLine();
String[] st=str.split(" ");
int n=Integer.parseInt(br.readLine());
Dice dice1 =new Dice(st);
for(int i=0;i<n;i++){
st=br.readLine().split(" ");
for(int j=0;j<6;j++){
if(j==3)dice1.roll('E');
if(st[1].equals(dice1.front))break;
dice1.roll('N');
}
while(true){
if(st[0].equals(dice1.top))break;
dice1.roll('W');
}
System.out.println(dice1.right);
}
}
}
class Dice{
String top,front,right,left,back,bot;
Dice(String[] s){
top=s[0];
front=s[1];
right=s[2];
left=s[3];
back=s[4];
bot=s[5];
}
void roll(char c) {
// 現在のサイコロの状態を一時変数に保存
String tempTop = this.top;
String tempBot = this.bot;
String tempFront = this.front;
String tempBack = this.back;
String tempLeft = this.left;
String tempRight = this.right;
// 転がす方向に応じて面を更新
switch (c) {
case 'N': // 北に転がす
tempTop = this.front;
tempBot = this.back;
tempFront = this.bot;
tempBack = this.top;
break;
case 'S': // 南に転がす
tempTop = this.back;
tempBot = this.front;
tempFront = this.top;
tempBack = this.bot;
break;
case 'W': // 西に転がす
tempTop = this.right;
tempBot = this.left;
tempRight = this.bot;
tempLeft = this.top;
break;
case 'E': // 東に転がす
tempTop = this.left;
tempBot = this.right;
tempRight = this.top;
tempLeft = this.bot;
break;
}
// 更新された面をオブジェクトの状態に反映
this.top = tempTop;
this.bot = tempBot;
this.front = tempFront;
this.back = tempBack;
this.left = tempLeft;
this.right = tempRight;
}
}
String getd1(){return this.top;}
}
- なお、今回は入力の読み取りにBufferReaderを使っているが、別解①ではScannerを使っている
- Scanner は BufferedReader よりも処理が遅い(nextLine() を頻繁に呼び出す場合)
IOException を明示的に扱わなくてもいいが、FileNotFoundException の処理が必要。
- BufferedReader はバッファリング機能があり、Scanner よりも高速だが、readLine() で取得した文字列を数値に変換するには、Integer.parseInt(line) などの処理が必要。また、コードもやや冗長
問3.サイコロ③
Dice I と同様の方法で、入力された整数から2つのサイコロをつくります。これらが同一のものか判定するプログラムを作成してください。Dice I の方法でサイコロを回転させ、2つのサイコロのそれぞれの6方向から見た整数が全て一致するとき、同一のサイコロとみなします。
- input
1行目に1つ目のサイコロの各面の整数が、ラベルの順番に空白区切りで与えられます。
2行目に2つ目のサイコロの各面の整数が、ラベルの順番に空白区切りで与えられます。 - output
2つのサイコロが同一ならば "Yes" と、異なるならば "No" と1行に出力してください。
import java.util.*;
class Dice {
int[] faces;
//コンストラクタ
Dice(int[] faces) {
this.faces = faces.clone();
}
void rollNorth() {
int temp = faces[0];
faces[0] = faces[1];
faces[1] = faces[5];
faces[5] = faces[4];
faces[4] = temp;
}
void rollSouth() {
int temp = faces[0];
faces[0] = faces[4];
faces[4] = faces[5];
faces[5] = faces[1];
faces[1] = temp;
}
void rollEast() {
int temp = faces[0];
faces[0] = faces[3];
faces[3] = faces[5];
faces[5] = faces[2];
faces[2] = temp;
}
void rollWest() {
int temp = faces[0];
faces[0] = faces[2];
faces[2] = faces[5];
faces[5] = faces[3];
faces[3] = temp;
}
void rollRight() {
int temp = faces[1];
faces[1] = faces[3];
faces[3] = faces[4];
faces[4] = faces[2];
faces[2] = temp;
}
// 修正したequalsメソッド
boolean equals(Dice other) {
// 配列の値を直接比較
return Arrays.equals(this.faces, other.faces);
}
// 現在の状態をコピーして新しいDiceを作成
Dice copy() {
return new Dice(this.faces);
}
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int[] input1 = new int[6];
int[] input2 = new int[6];
for (int i = 0; i < 6; i++) input1[i] = sc.nextInt();
for (int i = 0; i < 6; i++) input2[i] = sc.nextInt();
Dice dice1 = new Dice(input1);
Dice dice2 = new Dice(input2);
// top の位置を6通り試す
for (int i = 0; i < 6; i++) {
// 毎回コピーを作成して回転させる
//copy() を使い、dice2 の元の状態を保持
//コピー (testDice) を使って回転処理を行い、元の dice2 を壊さない
Dice testDice = dice2.copy();
if (i == 1) testDice.rollNorth(); // top = front
else if (i == 2) testDice.rollSouth(); // top = back
else if (i == 3) testDice.rollEast(); // top = left
else if (i == 4) testDice.rollWest(); // top = right
else if (i == 5) {
testDice.rollNorth();
testDice.rollNorth(); //rollNorth() を 2回(底面を上面に)
}
// front の向きを 4 通り試す
for (int j = 0; j < 4; j++) {
if (dice1.equals(testDice)) {
System.out.println("Yes");
return; //returnでmain メソッドの実行が終了
}
testDice.rollRight(); //rollRight() を4回回して、frontの向きをすべて試す。
}
}
System.out.println("No");
}
}
//入力を受け取り、2 つのサイコロ (dice1, dice2) を作成
//dice2 をコピーして testDice に格納
//top の向きを 6 通り試す
//front の向きを 4 通り試す
- 二つの配列の参照ではなく、中身を比較したいとき→2引数の Arrays.equals() メソッドを使う
- プログラムの判定の流れでは、サイコロ dice2 を 色々な向きに転がして一致するかを調べる
しかし、一度転がすと元に戻せないので、毎回 dice2 のコピーを作り、それを転がしてチェックする - もし copy() を使わずに dice2 を直接転がしたら、dice2 の状態が変わり続けて元の状態に戻せなくなる
→一度回転させた dice2 の状態が次の i のループでそのままになってしまう!
→本来試すべき組み合わせとは違う状態になり、正しい比較ができなくなるという問題が発生する - トップ面(top)を変更する
→変更後の状態が一致しているかチェックする
→フロント面(front)を右に回転させる
→これを繰り返す - return があることで、一度「Yes」が出力されたら、もうその後の回転処理を続ける必要がなくなる
- this は現在のオブジェクト(このメソッドが呼ばれた Dice オブジェクト)→現在のDiceオブジェクトの配列を新しく作ったサイコロオブジェクトに使い現在の Diceオブジェクト と同じ面の情報を持つようにする
重要:配列の共有渡しとコピー
- 配列をメソッドに渡すと、配列そのものではなく、そのメモリ上のアドレス(参照)が渡される。これにより、呼び出し元(元の配列)と、呼び出し先(メソッド内の配列や変数)が同じメモリ領域を指していることになり、配列データを共有する
class Dice {
int[] faces;
// Diceクラスのコンストラクタ
Dice(int[] faces) {
this.faces = faces; // ここで faces 配列への参照を渡して配列を共有する
}
}
public class Main {
public static void main(String[] args) {
int[] faces = {1, 2, 3, 4, 5, 6}; //8231
Dice dice = new Dice(faces); // faces 配列を渡す
faces[0] = 10; // faces 配列(8231番地)の最初の要素を変更
System.out.println(Arrays.toString(dice.faces)); // [10, 2, 3, 4, 5, 6]
}
}
- Diceオブジェクトにfaces 配列を渡す= 8231番地を渡す→this.faces = faces;よりそのオブジェクトのfaces配列は8231番地にある配列、つまり{1, 2, 3, 4, 5, 6}と同じ
- faces[0] = 10; と変更した場合dice.faces も同じ配列を参照しているので、その変更が**dice.faces[0]にも反映される
- clone() メソッド→faces 配列の内容が新しい配列としてコピーされ、元の配列と新しい配列が別々に管理される
class Dice {
int[] faces;
// Diceクラスのコンストラクタ
Dice(int[] faces) {
this.faces = faces.clone(); // 9231番地に8231番地にある配列の中身をコピーして渡す
}
}
public class Main {
public static void main(String[] args) {
int[] faces = {1, 2, 3, 4, 5, 6}; //8231番地にある配列
Dice dice = new Dice(faces); // faces 配列を渡す
faces[0] = 10; // faces 配列の最初の要素を変更
System.out.println(Arrays.toString(dice.faces)); // [1, 2, 3, 4, 5, 6]
}
}
問4.サイコロ④
Dice I と同様の方法で、入力された整数からn個のサイコロをつくります。これらのサイコロが、全て異なるものかどうかを判定するプログラムを作成してください。同一かどうかの判定は Dice III の方法を用います。
-
Input
1行目にサイコロの個数nが与えられます。続くn行に各サイコロの面の整数が与えられます。
各サイコロについて各面の整数が、ラベルの順番に空白区切りで1行に与えられます。 -
Output
入力されたサイコロがすべて異なる場合 "Yes"、同じものが1組以上含まれる場合 "No" と1行に出力してください。
import java.util.*;
class Dice {
int[] faces;
Dice(int[] faces) {
this.faces = faces.clone();
}
void rollNorth() {
int temp = faces[0];
faces[0] = faces[1];
faces[1] = faces[5];
faces[5] = faces[4];
faces[4] = temp;
}
void rollSouth() {
int temp = faces[0];
faces[0] = faces[4];
faces[4] = faces[5];
faces[5] = faces[1];
faces[1] = temp;
}
void rollEast() {
int temp = faces[0];
faces[0] = faces[3];
faces[3] = faces[5];
faces[5] = faces[2];
faces[2] = temp;
}
void rollWest() {
int temp = faces[0];
faces[0] = faces[2];
faces[2] = faces[5];
faces[5] = faces[3];
faces[3] = temp;
}
void rollRight() {
int temp = faces[1];
faces[1] = faces[3];
faces[3] = faces[4];
faces[4] = faces[2];
faces[2] = temp;
}
boolean isSame(Dice other) {
// dice をあらゆる向きにして比較
for (int i = 0; i < 6; i++) {
Dice test = this.copy();
if (i == 1) test.rollNorth();
else if (i == 2) test.rollSouth();
else if (i == 3) test.rollEast();
else if (i == 4) test.rollWest();
else if (i == 5) {
test.rollNorth();
test.rollNorth();
}
for (int j = 0; j < 4; j++) {
if (Arrays.equals(test.faces, other.faces)) return true;
test.rollRight();
}
}
return false;
}
Dice copy() {
return new Dice(this.faces);
}
}
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Dice[] dices = new Dice[n];
for (int i = 0; i < n; i++) {
int[] faces = new int[6];
for (int j = 0; j < 6; j++) {
faces[j] = sc.nextInt();
}
dices[i] = new Dice(faces);
}
// すべての組み合わせを比較
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
if (dices[i].isSame(dices[j])) {
System.out.println("No");
return;
}
}
}
System.out.println("Yes");
}
}
- dices 配列には、それぞれの入力されたサイコロのオブジェクトがすべて入っている