Java初心者の競技プログラミング日記

Dvorak配列でjavaを書いてます

AtCoder Beginner Contest 094

※今回は不参加で、問題はすべてコンテスト終了後に解きました。

A - Cats and Dogs

import static java.lang.System.*;
import java.util.*;
 
public class Main {
	static Scanner sc = new Scanner(System.in);
	public static void main(String[] args) {
		int a=sc.nextInt(), b=sc.nextInt(), x=sc.nextInt();
		
		if (x < a) out.println("NO");
		else {
			out.println(x-a<=b?"YES":"NO");
		}
	}
}

猫の最小値はa匹なので、それ未満の数の猫を指定された場合は条件分岐で弾く。
それ以上を指定された場合は、b匹のなかにx-a匹の猫がいることを証明する、つまりx-aがb以下であればよいので、それを条件式で処理して終わり。


B - Toll Gates

import static java.lang.System.*;
import java.util.*;
 
public class Main {
	static Scanner sc = new Scanner(System.in);
	public static void main(String[] args) {
		int n=sc.nextInt(), m=sc.nextInt(), x=sc.nextInt();
		int[] ar = new int[n+1];
		for (int i=0; i<m; i++) {
			int temp = sc.nextInt();
			ar[temp]++;
		}
		
		int costL=0, costR=0;
		for (int i=x; i>=0; i--) costL += ar[i];
		for (int i=x; i<=n; i++) costR += ar[i];
		
		out.println(Math.min(costL,costR));
	}
}

配列を用意して、初期地点から左端(0)に向かうときのコストと、右端(n-1)に向かうときのコストを比較して、少ないほうを出力する。


C - Many Medians

import static java.lang.System.*;
import java.util.*;
 
public class Main {
	static Scanner sc = new Scanner(System.in);
	public static void main(String[] args) {
		int n = sc.nextInt();
		int[] ar = new int[n];
		int[] ar2 = new int[n];
		for (int i=0; i<n; i++) {
			ar[i] = sc.nextInt();
			ar2[i] = ar[i];
		}
		Arrays.sort(ar);
		
		int medianA = ar[n/2-1];
		int medianB = ar[n/2];
		for (int i=0; i<n; i++) {
			if (ar2[i] <= medianA) out.println(medianB);
			else out.println(medianA);
		}
	}
}

答えとなる値は2通りしかないな、というのはすぐに分かりました。

与えられる要素数は偶数なので、配列をソートし、左側の中央値(本来なら2値の平均をとりますが、暫定的に)と右側の中央値をまず求める。
次に、ソートする前の配列を回していって、値が左側の中央値以下ならば右側の中央値を、右側の中央値以下ならば左側の中央値を出力する。


D - Binomial Coefficients

import static java.lang.System.*;
import java.util.*;
 
public class Main {
	static Scanner sc = new Scanner(System.in);
	public static void main(String[] args) {
		int n = sc.nextInt();
		int[] ar = new int[n];
		for (int i=0; i<n; i++) {
			ar[i] = sc.nextInt();
		}
		Arrays.sort(ar);
		
		int max = ar[n-1];
		double target = max/2.0;
		double minDif = Integer.MAX_VALUE;
		int ans = 0;
		for (int i=0; i<n-1; i++) {
			double tempDif = Math.abs(ar[i]-target);
			if (tempDif < minDif) {
				ans = ar[i];
				minDif = tempDif;
			}
		}
		
		out.println(max+" "+ans);
	}
}

出力例1を見て、「最大値」と「最大値/2にいちばん近い値」を順に出力すればいいのでは、と思い、そのように書いたところACしました。
その後解説を読みましたが、何が何やら分からないので、やっぱり数学の知識をつけないといけないのだなと改めて思いました。


まとめ

とくになし

AtCoder Beginner Contest 093

※今回は不参加で、問題はすべてコンテスト終了後に解きました。

A - abc of ABC

import static java.lang.System.*;
import java.util.*;
 
public class Main {
	static Scanner sc = new Scanner(System.in);
	public static void main(String[] args) {
		String s = sc.next();
		boolean a=false,b=false,c=false;
		if (s.contains("a")) a = true;
		if (s.contains("b")) b = true;
		if (s.contains("c")) c = true;
		
		if (a && b && c) out.println("Yes");
		else out.println("No");
	}
}

boolean型の変数でabcそれぞれの出現を管理して、すべて出現したならばYesを出力する。
文字列を1文字ずつ分解して配列で受け取って、ソートしたときに"abc"と一致するかという方法もあるけれど、記述の手間を考えればどちらでもよいと思います。

追記:if (s.contains("a") && s.contains("b") && s.contains("c")) とすれば一行で書けますね……


B - Small and Large Integers

import static java.lang.System.*;
import java.util.*;
 
public class Main {
	static Scanner sc = new Scanner(System.in);
	public static void main(String[] args) {
		int a = sc.nextInt();
		int b = sc.nextInt();
		int k = sc.nextInt();
		TreeSet<Integer> set = new TreeSet<>();
		for (int i=a; i<=Math.min(b,a+k-1); i++) set.add(i);
		for (int i=b; i>=Math.max(a,b-k+1); i--) set.add(i);
		set.forEach(out::println);
	}
}

WAを一回出してしまいました。
問題を見たときに、範囲が重複するだろうなと思ってSetを持ち出したところまではよかったのですが、そのあとのループの回しかたでミスをしてしまいました。若干悪戦苦闘しつつ、ループの条件式をいじくり回してなんとかAC。


C - Same Integers

import static java.lang.System.*;
import java.util.*;
 
public class Main {
	static Scanner sc = new Scanner(System.in);
	public static void main(String[] args) {
		int a=sc.nextInt(), b=sc.nextInt(), c=sc.nextInt();
		int max = Math.max(a,b); max = Math.max(max,c);
		int sum = a+b+c;
		
		
		if (sum%2 == (3*max)%2) {
			out.println((3*max-sum)/2);
		}
		else {
			out.println((3*(max+1)-sum)/2);
		}
	}
}

以前に見たような問題だったけれど、ぱっと解法が思いつかなかったので解説を見ました。
ポイントは「操作によって3数の和の偶奇は変化しない」というところのようです。

公式の解説には「この下限が達成できることは簡単にわかるので、この問題を解くことができました」とあったのですが、いまいち理解できなかったので、解説してくださっている記事を探したところ、以下の記事にとても分かりやすい解説がありました。

AtCoder Beginner Contest 093 - forestelementsの日記

要は、3つの数が同値を達成する寸前のパターンが(どう操作しても)2通りしかなく、その2通りで場合分けすればよいみたいです。


D - Worst Case

またこんど


まとめ

一か月間もまともにプログラミングしないでいると、能力はそれなりに落ちるみたいです。具体的には、

・コードを記述するスピード
・文法
・問題の理解速度

などの能力にずいぶん衰えを感じました。これからは(たとえ忙しくても)サボらないようにしたいと思います。

久しぶりの更新

3月29日以来、ほぼ一か月間、ほとんど競技プログラミングに触れていませんでした。

理由は置いておいて、今日からまじめに再開したいと思います。

「一か月間競技プログラミングから離れると、どれだけ能力が落ちるのか」のモデルケースにでもなれれば幸いです。

【JavaFx入門】002.キーボード入力を取得する

キーボード入力を受け取って動作するJavaFxプログラムを書いてみたいと思います。

ここでは、上下左右のキーでウィンドウ内の画像を操作する、RPG風のサンプルプログラムを紹介します。

package Blog;

import java.io.File;
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyEvent;
import javafx.scene.paint.Color;
import javafx.stage.Stage;


public class Sample extends Application {
	
	//変数
	int[][] map = {
			{1,1,1,1,1},
			{1,2,0,0,1},
			{1,0,0,0,1},
			{1,0,0,0,1},
			{1,1,1,1,1},
	};
	int heroX = 1;
	int heroY = 1;
	Group root = new Group();
	Image wall = new Image(new File("C:\\roguelike\\image\\wall.gif").toURI().toString());
	Image floor = new Image(new File("C:\\roguelike\\image\\floor.gif").toURI().toString());
	Image hero = new Image(new File("C:\\roguelike\\image\\hero.gif").toURI().toString());
	
	
	//ステージ、シーンなどの設定
	@Override
	public void start(Stage stg) {
		stg.setTitle("title");
		Scene scene = new Scene(root, 300, 300, Color.BLACK);
		stg.setScene(scene);
		printMap();
		
		//sceceにキー受け取りメソッドをセットする
		scene.setOnKeyPressed(e -> press(e));
		
		stg.show();
	}
	

	//キーイベントを受け取って処理するメソッド
	void press(KeyEvent event) {
		map[heroY][heroX] = 0;

		//switchで分岐させると分かりやすく記述できる
		switch (event.getCode()) {
		case UP:
			heroY = Math.max(1,heroY-=1);
			map[heroY][heroX] = 2;
			break;
		case DOWN:
			heroY = Math.min(3,heroY+=1);
			map[heroY][heroX] = 2;
			break;
		case LEFT:
			heroX = Math.max(1,heroX-=1);
			map[heroY][heroX] = 2;
			break;
		case RIGHT:
			heroX = Math.min(3,heroX+=1);
			map[heroY][heroX] = 2;
			break;
		}
		
		//キー入力を受け取ったあと、描画する
		printMap();
	}
	
	
	//画面を描画する
	void printMap() {
		root.getChildren().clear();
		for (int i=0; i<5; i++) {
			for (int j=0; j<5; j++) {
				ImageView imv = new ImageView();
				if (map[i][j] == 1) imv = new ImageView(wall);
				else if (map[i][j] == 0) imv = new ImageView(floor);
				else if (map[i][j] == 2) imv = new ImageView(hero);
				
				//描画間隔は、画像の大きさによって調整
				imv.setX(1+j*16);
				imv.setY(1+i*16);
				root.getChildren().add(imv);
			}
		}
	}

	public static void main(String[] args) {
		launch(args);
	}
}

今回のプログラムでは3つの画像(hero.gif, wall.gif, floor.gif)を使用していますが、これについては、パソコンに標準で入っているものではありませんので、もしこのプログラムを起動してみたい場合は、各自で用意してください。大きさはそれぞれ16*16で、透過処理を施してあります。

画像を指定したフォルダに配置し起動すると、下のようなウィンドウが出現します。

f:id:tsukasa_d:20180329180531j:plain

上下左右のキーで、白い丸(主人公)を操作できます。茶色の点は床で、床の上には移動可能です。茶色の四角は壁で、壁の上には移動できません。

僕もまだ手探りの状態なので、拙いプログラムになっているかもしれませんが、JavaFxRPGを作ってみたいという方の参考になれば幸いです。

【JavaFx入門】001.最も単純なJavaFxプログラム

最も単純だと思われるJavaFxプログラム(ウィンドウを表示するだけ)を書いてみたいと思います。

package Blog;

import javafx.application.Application;
import javafx.stage.Stage;

public class Sample extends Application {
	
	@Override
	public void start(Stage stg) {
		stg.show();
	}
	
	public static void main(String[] args) {
		launch(args);
	}

}


StringBuilderやFXMLで作成すると多少違ってきますが、それらを使わない場合、この書き方がすべてのJavaFxプログラムの基本になります。

では次に、テキストが書かれたラベル(テキストや画像を貼り付けられるエリアだと思ってください)を表示するプログラムを書いてみたいと思います。

package Blog;

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.stage.Stage;

public class Sample extends Application {

	@Override
	public void start(Stage stg) {
		//文字列入りラベルを定義
		Label label = new Label("Hello JavaFx!");

		//レイアウトコンテナを定義
		Group group = new Group();

		//レイアウトコンテナgroupの中にラベルを入れる
		group.getChildren().addAll(label);

		//シーンを作成し、レイアウトコンテナを追加
		Scene scene = new Scene(group);
		
		//シーンをステージに追加
		stg.setScene(scene);
		
		//ウィンドウを可視化する
		stg.show();
	}

	public static void main(String[] args) {
		launch(args);
	}
	
}


今回は何もしませんでしたが、ラベルを定義したあとに、label.メソッド名(引数)と記述することで、ラベルにさまざまな変更を加えられます。具体的には、

・label.setTextFill(Color.WHITE); 文字色を白に
・label.setFont(Font.font("フォント名",フォントサイズ)); フォントとフォントサイズの変更

などがあります。



レイアウトコンテナ(オブジェクトを自動で配置してくれる枠のようなもの)については、今回はGroupを使用しました。Groupは自動配置をいっさい行わないレイアウトボックスです。具体的には、自らに属するすべてのオブジェクトを座標(0,0)に配置します。

オブジェクトの配置を手動で決めたいときは、このレイアウトボックスを使うとよいでしょう。

自動配置したいときは、縦に並べて配置してくれる「VBox」や、横に並べて配置してくれる「HBox」などを使います。



シーン(Scene)について。自分もまだあまり理解できていないのですが、JavaFxの構造は、根本部分から順に、

・Stage → Scene → SceneGraph

となっています。

Stageがウィンドウそのもの、Sceneがウィンドウ内部の基礎となるもの、SceneGraphがウィンドウ内部に実際に配置するオブジェクト、ということみたいです。Sceneにレイアウトコンテナ(SceneGraph)を1つ貼り付けて、その中にオブジェクト(SceneGraph)を入れていく、というのが基本的な使い方になります。

SceneGraphは入れ子構造になっていて、たとえば、「2つのラベルを格納したVBox」と「2つの画像を格納したHBox」を「VBox」に入れて表示するというようなとき、SceneGraphは

・VBox
 ・VBox
  ・label 01
  ・label 02
 ・HBox
  ・image 01
  ・image 02

のような構造になります。

これを上手く利用することで複雑なレイアウトのアプリケーションを作成することができますが、エディタ上でのレイアウト作成には限界があり、基本的にはSceneBuilderなどのGUIプログラミングツールを使うことになります。

ローグライクゲーム制作日記 2日目

現況

・わりと形にはなってきた
・コードが煩雑としてきた


今日やったこと

・街マップを作った
・階段上り下りの処理を作った
・アイテム欄を作った


次やること

・ランダムダンジョン生成はとりあえず後回し

・主人公のステータス充実
 →メンバに剣、鎧なども追加する
 →所持金
 →経験値、レベル

・それに伴う装備変更のシステムも作成

・店の処理
 →方向指定がめんどくさい(移動と話しかけるのを一緒にできない)ため、
  店の四近傍でTキーを押すとショップ画面に移行するように。
 →売却と購入。これもアイテム欄が必要。

・敵の移動AI
 →感知範囲+移動頻度+移動AI
 →弱い敵は感知範囲を狭くする
 →移動頻度についても同様
 →移動AIはめんどくさいので全部同じで(幅優先探索による最短距離)

・敵の能力
 →深い階層ほど強くなる(当然)
 →ボス以外はランダム

・弓矢とか魔法とか……
 →うーん無理
 →次作で
 →剣の範囲を広げるくらいだったらできるかな

・敵のグラフィック(つまり文字)
 →敵ごとに文字を変える?
  →可能
  →AtoZで、強いほどZに近いアルファベットとか
  →アイテム(I)と被ってるのでそこをとりあえず考える
 →敵の体力を色で表現したいが……
  →いまの表示方法(画面をまとめてラベルに貼りつける)だとできない。
  →Textオブジェクトを20*40個、画面に並べていくなどすれば実現可能
  →すごい重くなりそう

・扉の概念
 →一定の確立で進めるようになるマスと考えれば簡単に実装できるが……
 →保留

ローグライクゲーム制作日記 1日目

目標

JavaJavaFx)でローグライクゲームを作る
・挫折する可能性が高いので、最低限のものでいいからとにかく完成させる


今日やったこと

・ゲームの外枠作り(マップ、キー入力、敵配置など)
・サンプルマップの中を探索できるようにした
・敵をランダムに移動させるようにした
 →主人公との座標差で移動するだけなので、次は幅優先探索でつくるべし
 →それだと重くなるかも……?
・ステータス欄とメッセージ欄を作った

↓こんな感じ

f:id:tsukasa_d:20180328045354j:plain


次やること

・ランダムダンジョン生成はとりあえず後回し

・適当でもいいので街マップをつくる

・階段の昇り下りの処理。
 →ゲーム開始時は0F(街)
 →下り階段処理
  →0F~8Fにいる場合は、ダンジョン生成
  →9Fにいる場合は、ボスマップ生成
 →昇り階段処理
  →10F~2Fにいる場合は、ダンジョン生成
  →1Fにいる場合は、街生成

・アイテム欄の実現
 →かなりめんどくさい
 →取得したらリストに追加して……って感じか。

・主人公のステータス充実
 →メンバに剣、鎧なども追加する
 →所持金
 →経験値、レベル

・それに伴う装備変更のシステムも作成

・店の処理
 →方向指定がめんどくさい(移動と話しかけるのを一緒にできない)ため、
  店の四近傍でTキーを押すとショップ画面に移行するように。
 →売却と購入。これもアイテム欄が必要。

・敵の移動AI
 →感知範囲+移動頻度+移動AI
 →弱い敵は感知範囲を狭くする
 →移動頻度についても同様
 →移動AIはめんどくさいので全部同じで(幅優先探索による最短距離)

・敵の能力
 →深い階層ほど強くなる(当然)
 →ボス以外はランダム

・弓矢とか魔法とか……
 →うーん無理
 →次作で
 →剣の範囲を広げるくらいだったらできるかな

・敵のグラフィック(つまり文字)
 →敵ごとに文字を変える?
  →可能
  →AtoZで、強いほどZに近いアルファベットとか
  →アイテム(I)と被ってるのでそこをとりあえず考える
 →敵の体力を色で表現したいが……
  →いまの表示方法(画面をまとめてラベルに貼りつける)だとできない。
  →Textオブジェクトを20*40個、画面に並べていくなどすれば実現可能
  →すごい重くなりそう

・扉の概念
 →一定の確立で進めるようになるマスと考えれば簡単に実装できるが……
 →保留