電脳ルギア

コンピュータ・心理・オリックス

Java入門〜前編〜

前書き

ゴールデンウィーク中に新しい何かを勉強したいと思い、思い切ってJavaの勉強を始めました。
Javaを勉強すると決めた理由は以下の四点です。

今回は①基礎文法編という事で、Javaに関する豆知識と文法についておさらいします。

JavaJVM

Javaを動作させる上で、書いたプログラムはOSには依存しません。これはJava仮想マシン(=JVM)がソフトウェアとして存在するためです。
Javaで書いたソースコードJVMを通して、環境に依存する機械語に翻訳されます。実際は"ソースコード"をJavaコンパイラが"バイトコード"に、"バイトコード"をJVMが各OSに依存する機械語に翻訳されます。

JVMにより機械語翻訳される言語には他にもKotlinやScalaJythonなどが有名です。ただしCやC++のような完全コンパイラ言語に比べると、実行速度が遅いようですが、現代のメモリの進化により気にはならない程度です。

Java〜基礎文法〜

まずどこに書くねん

Javaはクラスブロックとメソッドブロックにより構成されており、初心者はメソッドブロックの内部から簡単なプログラムを記述します。

// クラスブロック
public class Helloworld {
  // メソッドブロック
  public static void main(String[] args) {
  // ここに記述
  }
}

出力

長ったらしいな〜。

System.out.println(("Hello world"));

Hello world

変数定義

doubleやfloatは浮動小数点型であり、doubleの方がfloatより厳密な計算ができる特徴があります。
変数型定義の前に"final"を追加する事で定数化できます。

final byte _byte = 126;
final short _short = 3000;
final int _int = 1000000;
final long _long = 10000000L;
final double _double = 0.23;
final float _float = 0.23F;
final boolean _boolean = true;
final char _char = '日';
final String _string = "hello World";

演算子

演算子での注意点は 除算(A/B)をした場合に、割り切れない場合、商は自動的に切り捨て値になります。

int num1 = 8;
int num2 = 8;
String str1 = "hello!  : ";
int num3 = num1 / num2;
System.out.println(str1 + num3);

hello!  : 1

加算処理(++を後ろ)

num4にnum1を代入した後、num1に加算処理をします。そのためnum4は加算される前のままです。

int num4 = num1++;
str1 = "num1++  : ";
System.out.println(str1 + "num1: " + num1);
System.out.println(str1 + "num4: " + num4);

num1++  : num1: 9
num1++  : num4: 8

加算処理(++を前)

num2を加算した後、num5にnum2を代入をします。そのためnum5は加算された状態です。

int num5 = ++num2;
str1 = "++num1  : ";
System.out.println(str1 + "num2: " + num2);
 System.out.println(str1 + "num5: " + num5);

++num1  : num2: 9
++num1  : num5: 9

型の自動変換

小さな型の変数から大きな型の変数に代入するとき、代入先の型に合わせてくれます。ただ代入先が小さな型だとコンパイラエラーを発生させるので注意が必要です。

それでもそうしても代入させたい時、キャストを活用します。強制的に型変換を行ってくれます。

double x = 10.3;
int y = (int) x;

型変換

よく利用するのは"文字列"→"整数型"、"整数型"→"文字列”のようなパターンです。この場合は以下の通りです。

//        文字列→整数
String str3 = "1010101";
int num6 = Integer.parseInt(str3);
System.out.println(num6 + 9);
//        整数→文字列
String str4 = String.valueOf(num6);
System.out.println(str4 + '9');

1010110
10101019

配列

String[] teams = new String[]{"braves", "bluewave", "buffaloes"};
System.out.println("要素数: " + teams.length);
int[][] list = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
System.out.println("list[0][1] : " + list[0][1]);

要素数: 3
list[0][1] : 2

引数

ファイル実行時にコマンド末尾にパラメータを入力するよ使用可能です。

System.out.println("引数番号 :"+args[0]);

プリミティブ型 vs 参照型

参照型は配列や文字列が当てはまります。
参照型では変数に値は格納されず、値を格納したメモリの場所が格納されます。

Hoge hoge = new Hoge();
String str = "fuga";

その為、文字列の比較をするとき、"=="で単純比較すると、値を格納するメモリ場所を比較することになるので、常にfalseを返します。

String b = "abc";
String c = new String("abc");
System.out.println(a == c); // false
System.out.println(a.equals(c)); // true

ただString型の変数もnewせずに、リテラルで代入できます。これを擬似プリミティブ型と呼ばれています。

String a = "abc";
String b = "abc";
System.out.println(a == b); // true
System.out.println(a.equals(b)); // true

制御(forとif)

String[] teams = new String[]{"braves", "bluewave", "buffaloes"};
for (int i = 0; i < teams.length; i++) {
      System.out.println((i + 1) + "番目の要素: " + teams[i]);
 }

1番目の要素: braves
2番目の要素: bluewave
3番目の要素: buffaloes

for文とif文を使って遊んでみます。

for (int j = 1; j < 10; j++) {
        String str8 = "";
        for (int k = 1; k < 10; k++) {
            if (k == j || k + j == 10) {
                str8 = str8 + "  " + " ";
             } else {
                 str8 = str8 + String.valueOf(j) + String.valueOf(k) + " ";
             }
         }
         System.out.println(str8);
    }

   12 13 14 15 16 17 18    
21    23 24 25 26 27    29 
31 32    34 35 36    38 39 
41 42 43    45    47 48 49 
51 52 53 54    56 57 58 59 
61 62 63    65    67 68 69 
71 72    74 75 76    78 79 
81    83 84 85 86 87    89 
   92 93 94 95 96 97 98  

メソッド

メソッドはmainの外で定義して、実行に関してはmain内で記述しました。
今回は一例ですが、メソッドは後にオブジェクト指向で非常に重要な概念となります。

メソッド名は同じ名前(join)ですが、引数となる変数・型の違いで区別できます。
これをオーバーロード(多重定義)と呼びます。

static String join(String name, int number) {
        String str9 = name + " : " + String.valueOf(number);
        return str9;
}

static void join(int[] list) {
        String ans = "Starting Member : ";
        for (int l = 0; l < list.length; l++) {
            ans = ans + list[l] + " ";
        }
        System.out.println(ans);
    }

public static void main(String[] args) {
       String answer = join("ICHIRO", 51);
       System.out.println(answer);

  int[] list2 = {6, 67, 34, 99, 1, 10, 24, 2, 31};
        join(list2);
}

ICHIRO : 51
Starting Member : 6 67 34 99 1 10 24 2 31 

AtCoder Beginner Contest 104・016・108・112 A・B問題

前書き

1週間ほどブログを書くのをお休みしていました。
色々と気持ちが切れてしまった為です。今後はAtCoder過去問と技術ブログを継続していこうと思います。

A問題(ABC108)

  • 難易度:灰
  • 時間:2分

atcoder.jp

K=int(input())
print((K//2+K%2)*(K//2))

B問題(ABC108)

  • 難易度:灰
  • 時間:15分

atcoder.jp

例に一つの長方形を描いてみて、端の4点の関係性を数式に落とし込みました。

x1,y1,x2,y2=map(int,input().split())
x3,y3=x2-(y2-y1),y2+(x2-x1)
x4,y4=x3-(x2-x1),y3-(y2-y1)
print(x3,y3,x4,y4)

A問題(ABC106)

  • 難易度:灰
  • 時間:2分

atcoder.jp

A,B=map(int,input().split())
print(A*B-(A+B-1))

B問題(ABC106)

  • 難易度:灰
  • 時間:15分

atcoder.jp

N=int(input())
_all=0
for i in range(10,N+1):
  count=0
  for j in range(1,i+1):
    if i%j==0 and i%2!=0:count+=1
  if count==8: _all+=1
print(_all)

A問題(ABC112)

  • 難易度:灰
  • 時間:1分

atcoder.jp

N=int(input())
if N==1: print('Hello World')
elif N==2:
  A=int(input())
  B=int(input())
  print(A+B)

B問題(ABC112)

  • 難易度:灰
  • 時間:7分

atcoder.jp

N,T=map(int,input().split())
_min,j=1001,0
for i in range(N):
  c,t=map(int,input().split())
  if t<=T:
    if c<=_min:
      _min=c
if _min<1001: print(_min)
else: print('TLE')

A問題(ABC104)

  • 難易度:灰
  • 時間:1分

atcoder.jp

R=int(input())
if R<1200: print('ABC')
elif R<2800: print('ARC')
else: print('AGC')

B問題(ABC104)f:id:electric-city:20200319193051j:plain

  • 難易度:灰
  • 時間:7分

atcoder.jp

あまり賢い解き方ではありません。条件を一つずつ潰していくアルゴリズムです。
TLEの心配は全くありませんので、実直に書きました。

import sys
S=input()

def stop():
  print('WA')
  sys.exit()
  
if S[0]!='A': stop()

count,_str=0,S[2:-1]
for i in _str:
  if i=='C':
    count+=1
    if count>1: stop()   
for i in S: 
  if not (i=='A' or i=='C'):
    if i.isupper(): stop()
      
if count==0: print('WA')
else: print('AC')

AtCoder Beginner Contest 199 A〜C問題

前書き

AtCoder Beginner Contest 199に参加しました。

A問題

  • 時間:2分
  • 難易度:灰
A,B,C=map(int,input().split())
if A**2+B**2<C**2: print('Yes')
else: print('No')

B問題

  • 時間:4分
  • 難易度:灰

atcoder.jp

配列A , Bに格納されている要素で全てのiでAi ≤ x ≤Biを満たすxの個数を探します。
A

N=int(input())
A=list(map(int,input().split()))
B=list(map(int,input().split()))
if min(B)-max(A)<0: print(0)
else: print(min(B)-max(A)+1)

C問題

  • 時間:70分
  • 難易度:?

T == 1の時は文字の入れ替え
T == 2の時は文字列前半と後半の入れ替えをします。

条件は以下の通りです。
1 ≤ Q ≤ 3*10**5
1 ≤ N ≤ 2*10**5

TLEした理由

普通に解こうとしたらTLEを連発しました。
なので工夫をしなければなりません。

文字列前半と後半の処理についてですが、配列からスライス処理はスライスする要素数をkすると、O(k)となります。
つまり最大でもO(2*10**5)になります。
ただでさえ1 ≤ Q ≤ 3*10**5なので、そりゃTLEしますよね・・・。

解決策

(1). 文字列前半後半入れ替え処理の回数を0 or 1回に

計算量を喰う文字列前半と後半の入れ替え処理の回数を減らしてやり必要があります。
→ 0 or 1回で済むようにします。
→ 文字列前半後半入れ替え処理の総回数は奇数の時は最後1回、偶数の時は0回になるようにします。
→ ただ途中にT == 1(文字入れ替え)が挟むので一見複雑です。

(2). 文字入れ替え処理に工夫を

処理時点の文字列前半後半入れ替え処理回数をcountと定義します。

count = 偶数の時
→文字入れ替え処理は通常通り
→ A-1番目とB-1番目を入れ替え

count = 奇数の時
→配列の要素を指定する時にNだけずらして入れ替えてやる
→ A-1-N番目とB-1-N番目を入れ替え
→ Nだけずらすことで(1)の矛盾を無くせる

これでもモヤモヤが残る場合は、問題のサンプルデータを使って実際に試してみてください。

N=int(input())
S=list(input())
Q=int(input())
count=0
for i in range(Q):
  T,A,B=map(int,input().split())
  if T==2:
    count+=1
  if count%2==0:
    k=S[A-1]
    S[A-1]=S[B-1]
    S[B-1]=k
  else:
    k=S[A-1-N]
    S[A-1-N]=S[B-1-N]
    S[B-1-N]=k

if count%2==0: print(''.join(S))
else: print(''.join(S[-N:]+S[:N]))

AtCoder Beginner Contest 109・111・113・114 A・B問題

前書き

AtCoder Beginner Contest 109・111・113・114 A・B問題を解きました。

A問題(ABC109)

  • 難易度:灰
  • 時間:2分30秒

atcoder.jp

import sys
A,B=map(int,input().split())
for i in range(1,4):
  if (A*B*i)%2!=0: 
    print('Yes')
    sys.exit()
print('No')

B問題(ABC109)

  • 難易度:灰
  • 時間:7分

atcoder.jp

最初に一つ前の文字列の末尾と次の文字列の頭が一致するかを確認し、その後に文字列の重複がないかをチェックしています。

import collections,sys
N=int(input())
lis=[]
for i in range(N):
  s=input()
  if i>0 and lis[-1][-1]!=s[0]:
    print('No')
    sys.exit()
  lis.append(s)

c = collections.Counter(lis)
if c.most_common()[0][1]>1: print('No')
else: print('Yes')

A問題(ABC111)

  • 難易度:灰
  • 時間:4分

atcoder.jp

もっと良い方法があるのではと考えましたが、時間を気にして妥協しました。

n=input()
a=''
for i in range(len(n)):
  if n[i]=='9': a+='1'
  elif n[i]=='1': a+='9'
print(a)

B問題(ABC111)

  • 難易度:灰
  • 時間:3分

atcoder.jp

N=int(input())
if N%111==0: print(N)
else: print((N//111+1)*111)

A問題(ABC113)

  • 難易度:灰
  • 時間:2分

atcoder.jp

X,Y=map(int,input().split())
print(int(X+Y/2))

B問題(ABC113)

  • 難易度:灰
  • 時間:6分

atcoder.jp

入力値を一つずつ確認しながら、理想値との誤差が最小値となる拠点番号を探します。

N=int(input())
T,A=map(int,input().split())
_min,ans=100000000000,100
H=list(map(int,input().split()))
for i in range(N):
  if abs((T-H[i]*0.006)-A)<_min:
    _min=abs((T-H[i]*0.006)-A)
    ans=i+1
print(ans)

A問題(ABC114)

  • 難易度:灰
  • 時間:30秒

atcoder.jp

X=int(input())
if X==3 or X==5 or X==7: print('YES')
else: print('NO')

B問題(ABC114)

  • 難易度:灰
  • 時間:7分

atcoder.jp

与えられた文字列から1つずつずらしながら、753との差が最小となる場合を探します。

S=input()
_min=1000000000
for i in range(int(len(S)-2)):
  _min = min(abs(int(S[i:i+3])-753),_min)
print(_min)