読書は人間の夢を見るか

平々凡々な社会人の読書と考えたこと。本文・写真についてはCC-BY-SA。当然ながら引用部分等の著作権は原文著者に属します。

文系初心者が競技プログラミングをやってみた話~AtCoder Beginner Contest 139編~

前回、AtCoderなどを楽しんでいると書きました。

AtCoderというのは、競技プログラミングのサイトです。

atcoder.jp

競技プログラミングというのは、プログラミングを使って、パズルのような問題を早く、正確に解く競技だと考えて良いかと思います。

将棋で言えば、詰将棋でしょうか。

問題に対して、解法(どうすれば解けるか)を思いつく発想力と、

それを正確に速く効率的なプログラムに落とし込む実装力が問われてなかなかおもしろいです。

私は、むかーし、Pythonという言語を、Couseraのコースで習っただけのド素人ですが、たまに開催される大会に参加して楽しんでいる、というわけです。

 

この日曜日にもコンテストがあって参加してみました。

問題はこちらから御覧ください。

Tasks - AtCoder Beginner Contest 139

 

どんな感じで解いていったのか、メモ代わりに残してみます。

 

A問題:

 

  1. S = str(input())
  2. T = str(input())
  3. cnt = 0
  4. for i in range(3):
  5. if S[i] == T[i]:
  6. cnt = cnt +1 #cnt += 1
  7. print(cnt)

 2つの文字列を比較してマッチする分の個数をチェックする問題でした。

思いついたとおりに書いてみましたが、forの部分は、zip()などを使って簡潔に書けたかもしれません。

 

B問題:

 

  1. A, B = map(int, input().split())
  2. cnt = 0
  3. num = 1
  4.  
  5. while num < B:
  6. cnt = cnt + 1 #cnt += 1
  7. num = num + A - 1 #num += A -1
  8. print(cnt)

 電源タップの必要な個数を考える問題。

実際タコ足は避けたほうが良いのでしょうが。ループを使って書いてしまいましたが、

本当は割り算のほうがすっきりするのでしょう。

 

C問題:

普段はこのへんから難しくなってくる気がするのですが、今回は割とすんなり行きました。

  1. N = int(input())
  2. H = [int(i) for i in input().split()]
  3. mx = 0
  4. tmp = 0
  5. #print(H)
  6. for i in range(N-1):
  7. if H[i] >= H[i+1]:
  8. tmp = tmp + 1
  9. if tmp > mx:
  10. mx = tmp
  11. else:
  12. tmp = 0
  13. print(mx)

階段を下っていく問題。

下れるところまで下っていって、最大値をmxに記録していく、という単純な方法でも間に合いました。

 

D問題:

Cまで行けばいいかと、Dの前に風呂に入りましたが、Dも行けました。

数列のModの最大値を求める問題。

  1. N = int(input())
  2.  
  3. print(N*(N-1)//2)

といっても余り褒められたものではなく。

(1、2、3・・・N)とあったときに、一つずらせば最大値だろうな、というのは、直感的にはわかったのですが、最初は、並べ替えた数列Pを作って、更に、最後尾を除いてsum()みたいなことをやってしまっていたのです・・・

そうすると計算時間がかかってしまって、タイムオーバー(TLE)。

単に等差数列の和なので、そのまま示してやればよかったですね。

 

E問題:

リーグの対戦順が可能か考える問題。

ループを回して、先頭からpop(リストから削除)してけばできるのかな~とか思ってたのですが、気力が起きずに撤退。popは先頭から削除するのは、めっちゃ遅いらしいとか、リストを裏っ返せばええんや、とか紆余曲折を経て、最終的には以下の形で通りました(後日)。

  1. import copy
  2. import collections
  3.  
  4. N = int(input())
  5. A=
  6. #結局dequeというやつを使うことにした。両端からの削除が早いらしい
  7. for j in range(N):
  8. A.append(collections.deque(int(i) for i in input().split()))
  9.  
  10. day = 0
  11. empty = [0] * N
  12. #一日目は全員を見て、次の日は前日に状況が変わった人だけみることに
  13. s = [i for i in range(N)]
  14.  
  15. #whileループ一回を一日と考える
  16. while N==N:
  17. q =
  18. day +=1
  19. for i in s:
  20. #i行目が空なら空であることを示すemptyリストを1に
  21. if len(A[i]) == 0:
  22. empty[i] = None
  23. #iが既にqに入っていればパス
  24. elif i in q:
  25. pass
  26. #i行目の末尾の数字で示された相手方が一致していればキューに追加
  27. elif int(A[int(A[i][0])-1][0]) == i+1:
  28. q.append(i)
  29. q.append(int(A[i][0])-1)
  30. s = copy.copy(q)
  31. #ループ後にqが空ならそれ以上進めないので判定
  32. if len(q) == 0:
  33. if not(0 in empty):
  34. print(day-1)
  35. exit()
  36. else:
  37. print(-1)
  38. exit()
  39. #そうでなければキューに追加されている行の末尾を削除する
  40. else:
  41. for d in q:
  42. A[d].popleft()

 

 これでもpythonではなくpypyというやつでぎりぎりでした(pypyのが若干早いらしい。)。

本来的には、グラフ?の最長経路問題として解けるらしいのですが、いまいち枝とか経路とかの書き方のイメージが掴めていません。勉強しなきゃですね。

 

F問題:

ベクトルの合成の問題なんだろうなーと眺めていましたが、角度でソートする方法を考えてる間に、エネルギー切れに。

あとの部分は、普通に書けばいいだけだったので、粘ればよかったかもしれない。

 

  1. import math
  2.  
  3. N = int(input())
  4. A = []
  5. for j in range(N):
  6. A.append([int(i) for i in input().split()])
  7. #arctanでY、Xから角度を求める。
  8. for engine in A:
  9. engine.append(math.degrees(math.atan2(engine[1], engine[0])))
  10.  
  11. if len(A)==1:
  12. print(math.sqrt(A[0][0]**2 + A[0][1]**2))
  13. exit()
  14. #角度順にソートして、2周分のリストに。
  15. A.sort(key=lambda x: x[2])
  16. A = A + A
  17.  
  18. mx = 0
  19. for i in range(N):
  20. X = A[i][0]
  21. Y = A[i][1]
  22. temp = 0
  23. for j in range(i+1, i+N):
  24. if (math.sqrt(X**2 + Y**2)) > mx:
  25. mx = math.sqrt(X**2 + Y**2)
  26. X = X + A[j][0]
  27. Y = Y + A[j][1]
  28. if temp> math.sqrt(X**2 + Y**2):
  29. break
  30. temp = (math.sqrt(X**2 + Y**2))
  31. if (math.sqrt(X**2 + Y**2)) > mx:
  32. mx = math.sqrt(X**2 + Y**2)
  33.  
  34. print(mx)

 あとから何も考えずに書いたので、多分余計な行とかがたくさんある。

 

Pythonを使うだけでもいろんなパズルが解けて楽しいので、みなさんもよろしければぜひ。

詳しい人は優しく教えて下さいw