塩見周子の徒然日記

自分のことを塩見周子と思い込んでいるオタクです

2/21 AtCoder(アルファベットのリスト、二進法表記、深さ優先探索)/ウニ

 AtCoder

解くに至った問題(4問)

ABC:014B/058C/064C/070C

 

 

ABC014B

与えられた数Xの二進法表記を求めるプログラム

X = int(input())

bi = ''

p = X
for i in range(1,n+1):                 #2**n > Xとなるようにnを選ぶ
    if 2**(i-1) <= X < 2**i:
        k = i
        break
for j in range(k):
    if p >= 2**(k-j-1):
        p -= 2**(k-j-1)
        bi += '1'
    else:
    bi += '0'

 

ABC058C

aからzまでの26文字のアルファベットが入ったリストが欲しいなら、

alp = [chr(ord('a') + i) for i in range(26)]

とすればよい

 

 解けなかった問題

ABC054C

昨日のUF木を使って解く問題かな~とか安直に考えてたら、今回は除く辺のパターンが2**(N**2)くらいあるのでループで書くのがめちゃんこ難しい

ここでは深さ優先探索(DFSというらしい)を利用して解くのがよいとのこと

島N個、島と島を結ぶ橋をM本とする

(問題では島は1,2,3......と番号が振られていたが、ここでは0,1,2......と扱う)

 

N, M = map(int, input().split())
G = [[] for i in range(N)]    #G[i]は、i番目の島と結ばれている島の番号を格納する
for i in range(M):
    a, b = map(int, input().split())  #↑のやり方で、橋で結ばれた島をリストに入れていく
    G[a - 1].append(b - 1)
    G[b - 1].append(a - 1)
cnt = [0]            #cntは、一筆書きできる場合の数を表す
def dfs(V, s):                              #リストVに着目、sは一筆書きをスタートする島である
    V[s] = 1                                  #sの島を訪れたことを示すため、まずV[s]を1にする
    if sum(V) == N:       #全ての島を訪れ終わると、リストVの要素は1で尽くさ          

                れているハズ
        cnt[0] += 1                         #そのような場合は今回の題意に適するので、cntを+1
    else:            #リストVが1で尽くされていない場合は、
        for adj in G[s]:      #今はs番目の島を見ているため、s番目の島と結ばれた島

                  を次に考える(訪れる)
            if V[adj] == 0:                #s番目の島と結ばれた島がVのリストで0になっていたら
                dfs(V[:adj] + [1] + V[adj + 1:], adj) #その島を1(訪れたことを表す)にする

                   さらにsをadj(次に訪れた島の番号)に変更
dfs([0] * N, 0)          #今回は、これを0番目の島からスタートさせる 
print(cnt[0])

 

例えば

3 3

1 2

1 3

2 3

という入力であれば、島1,2,3が三角形のようにつながっていることになるが、

これを上のプログラムに当てはめると、

 

島0(1)から一筆書きをスタート→

V = [1,0,0]に→隣接している島は島1(2)と島2(3)なので、こいつらを次に見て、V = [1,1,0] とV = [1,0,1]と新たに二つのリストを作る→

それをもとに、V = [1,1,0]についてdfs(V,1)とV = [1,0,1]についてdfs(V,2)を行う→

dfs(V,1)については、島1(2)と隣接する島が島0(1)と島2(3)であるが、V[0] == 1だからこれはもう見ないで、V[2] == 0についてのみ考える→

結局新たにV = [1,1,1]というリストができる これはsum(V) == 3を満たしているため、島0からスタートしてすべての島を訪れたことになっている→

これは題意に適するため、カウンターを1増やす→

dfs(V,2)についても同様の事から、カウンターの値が1増える 最終的に2パターンの経路で一筆書きができることがわかる

 

また一人で作りなおしてみます

atcoder.jp

今回もまたじゅっぴーさんにお世話になりました ありがとうございました

 

 

 

 ウニ

f:id:saguh:20190222025554j:plainf:id:saguh:20190222025609j:plain

昨日二週間ぶりくらいにやって今日アプデがあったのでやってきました

マップの新曲は二つしか解禁できてないですがどっちも曲も良くて楽しかったしキャラも可愛いしで文句ありません  Techno Kitchen脳トレやめろ

 

CITRUS MONSTERが問題で、多分10回くらいやったんですけど6954で止まってしまい鳥は乗りませんでした  早いトリルとかどうやって力入れて叩けばいいっちゅうねん  気合いで乗り切るしか無いですね

 

2/20 AtCoder(Union Find)

AtCoder

解くに至った問題(2問)

ABC:076C

AtCoder Typical Contest001:B

 

AtCoder Typical Contest001:B

Union Find木についての問題 これを解く前にABC075Cの連結判定の問題をにらみつけて時間を溶かしていた そういうのの解法はいろいろあるけど、辺の連結を判定するにはUF木がよいとのこと(チーター本のp.370くらいに載ってた これマジでC問題か?)

 

UF木のUの字も聞いたことのないプログラミングド素人が適当にかみ砕きながらあくまで自分のためだけに説明する

 

UFとは社会である 子会社のもとをたどれば親会社に、そして部下の上には必ず上司がいるように.......

 

簡単に言えばどの要素とどの要素が連結しているか(この連結は、何か他の要素を介して辿れるものもふくめて)を便利に扱えるもの 

予めゴールを決めておいて、同じ集合の要素であれば、どの要素からも遡っていった先がそのゴールにたどり着くような設計になっている このゴールの事を根(多分「ね」って読むんでしょう)という これが集合の要素を代表している値である

 

要素がn個あり、m本の辺で連結されているとしよう

まず、par(多分parentの略)とrankという空のリストを用意しておく

それから、

par = [0,1,2,......,n-1]  #n個の要素

rank = [0,0,0,......,0] #n個の要素

とする

parは、par[i]を上にたどっていった先の「根」が何かを表すものである

rankは、その「根」である要素jに対し、rank[j]が根の深さを表す。木のように要素が辺で連なっていると考えたとき、末端の要素から根がめっちゃ離れていればrank[j]は大きくなる

UFを扱うための操作として、findとuniteとsameという三つがある

 

find(x,par)

これは、要素xがparの中で、どれを根としているのかを表している したがって

def find(x,par):

  if x == par[x]:        #これはx自身が根であることを表している

    return x

  else:

    return find(par[x],par)       #par[x]はxの上にあるものを表す par[x]を辿ることで、最  

                                               終的にxの根が見つかる

 

unite(x,y,par,rank) 

これは、要素xと要素yが属する集合をくっつける操作である

def unite(x,y,par,rank):

  x = find(x,par)

  y = find(y,par)           #x,yの根を見つける

  if x != y:       #x,yの根が違えば、

    if rank[x] < rank[y]:   #根っこの深さを比較し、根っこが深い方に浅い方をくっつける

       par[x] = y     (この場合、xの根をyに変更する)

   else:

       par[y] = x

       if rank[x] == rank[y]:  #根っこの深さが同じ場合は片側のランクを+1する

          rank[x] += 1

 

same(x,y,par)

これは、x,yの根を比較するための操作である

def same(x,y,par):

  return find(x,par) == find(y,par)     #Trueなら1、Falseなら0が返ってくる

 

これらを組み合わせれば、晴れてUF木の扱いができる

例えば、AtCoder Typical ContestのB問題のように一つずつクエリが与えられた際には、①要素と要素をつなげる場合にはuniteを使えばよいし、②要素と要素が連結しているかどうかを判定する場合にはsameを使って判定すればよい

(今回の実装は0インデックスなので、例えば島の番号が1から始まっていたりしたら、入力を-1するといったことに注意する)

 

だから、ABC075Cの問題は、縦に与えられた入力を「要素を辺でつなげる操作」と解釈し、例えば1番目の辺を除いたすべてをUF木としたとき、連結していれば(つまり、取り除いた1番目の辺が橋でないならば)すべての要素の根が一致するはずであると考えることで、橋の本数は、それをM番目の辺までやったときに、連結していない(根がすべての要素で一致しなかった)UF木の数を数えてやれば求まると分かる また今度やってみます

 

今回もじゅっぴーダイアリーにお世話になりました ありがとうございました

juppy.hatenablog.com

2/19 AtCoder(二分探索(bisect)、変数を文字で指定した後計算するeval、多重ループを抜けるexit())

AtCoder

解くに至った問題(7問)

ABC:009B/010B/057C/061C/096D

ARC:059D/084C

 

ARC084C

3つのリストA,B,Cを所持し、1<=i<=Nに対し、Aの中からBのi番目の要素より小さいもの、Cの中からBのi番目の要素より大きいものの個数をそれぞれ求め、掛け合わせた後でその合計を求めていくという方針

問題を眺めれば方針は立つものの、実装するときに1つのBの要素に対しA,Cの要素N個を見ていたらN^2の計算量になりTLE そこで、今回は二分探索を使う

Python3では二分探索のライブラリが存在する

import bisectを一行目に書いてから使う

bisectには、bisect_leftとbisect_rightが存在する。どちらも、検索したい数字がソート済み(昇順)リストの中になければ、リストの中にその数字を入れると何番目に入るかを教えてくれる(インデックスを返す)が、1つ以上存在したときにその扱いが異なってくる

①bisect_left 

これは「ある数字未満の数字がリストの中にいくつあるか」という認識でよい 数字が既存の値の左側に入る

 

A = [1,2,4,4,5,6]に対し、

k = bisect.bisect_left(A,3)とすれば、

k = 2 と返ってきます   (A = [1,2,3,4,4,5,6]で、3が入るとしたらリストの2番目)

また、

k = bisect.bisect_left(A,4)とすれば、

k = 2 と返ってきます   (A = [1,2,4,4,4,5,6]で、既存の4の左側に入る)

 

②bisect_right

これは「ある数字以下の数字がリストの中にいくつあるか」という認識でよい 数字が既存の値の右側に入る

 

A = [1,2,4,4,5,6]に対し、

k = bisect.bisect_right(A,3)とすれば、

k = 2 と返ってきます   (A = [1,2,3,4,4,5,6]で、3が入るとしたらリストの2番目)

また、

k = bisect.bisect_right(A,4)とすれば、

k = 4 と返ってきます   (A = [1,2,4,4,4,5,6]で、既存の4の左側に入る)

 

ちなみに、bisect.insort_left(A,n)とすれば、leftで行ったのと同じ手順で、nをリストAの中に入れてくれる もちろんbisect.insort_right(A,n)もある

 

ARC059D

アンバランスな文字列

文字列が与えられて、その部分列の中に同じアルファベットが部分列の長さの過半数を占めている部分を抜き出せという問題

ろくに考えもせず解答を見たところ、XXもしくはXYXという部分列が存在しなければアンバランスな文字列は存在しないとのこと 

それはそう 

過半数を占めればよいので長さ2のうちの2もしくは3のうちの2を同じ文字が占めているところがあればそこを抜けばよいし、なければ無いと返せばよい

別にめちゃんこ長い部分を抜き出す必要はないのでこの程度で十分 もっと頭を使おうね

 

解けなかった問題

Tenka 1 Programmer Beginner Contest2018:C

ABC:079C

 

天下一C

数列で、隣り合う数の絶対値の合計を最大化する問題 リストで保持した値をソートし、例えば要素が5個であれば

p1 <= p2 >= p3 <= p4 >= p5

とするのか

p1 >= p2 <= p3 >= p4 <= p5

とするのかを二通り確かめる

さらに、プラスは下の場合、p1+2*p3+p5となるので、p3にはリストで最もでかいのを持ってくるべき

などの工夫を使って解く問題 こんがらがって一生できなかったので次の機会に

 

ABC079C

与えられた文字列の千の位、百の位、十の位、一の位の数字に対し、足し算もしくは引き算をした後で、答えが7になるような数式を出力しろという問題

cal = N[0]+op1+N[1]+op2+N[2]+op3+N[3]

と置くと(opnは演算子)これはあくまで文字列である

ここで、eval()を使うと、eval()は文字列を計算してくれる つまり

return(eval('1'))

は数字の1が返ってくるし、

return(eval('1+2'))

は数字の3が返ってくる

ここでは、 
if eval(cal) == 7:

  print(cal+'=7')

とすることで、calという文字列を計算してくれる

(cal+'=7'とすることに注意 calと=7は文字列なので接続する必要がある)

printではN[0]などに値が入る

 

また、

for i in range(N):

  for j in range(N):

みたいな、for文が連なっている時には、breakで抜けようとすると、for文の数だけbreakを書かないといけなくなる

exit()を使うことで、この手間を省き、一番内側のループをexit()で抜ければすべてのfor文のループを抜けることができる

note.nkmk.me

 

 

2/18 AtCoder(boolean配列、リストのn番目でソート)

AtCoder

解くに至った問題(8問)

ABC:066C/068C/082C/085D/088C/108C/109C/113C

 

ABC068C 

二つの島を行き来する定期便があり、往復する島がリストで与えられるので、一回の乗り換えで、島1→島i→島Nと行くことができるかを判定する問題

一個一個のリストを調べて[1,i]と[i,N]の存在を調べるのは非効率なので、ここではTrue、Falseを持ったリストを二つ作り、照らし合わせることを考える

具体的には、FalseをN+1個持ったリストJ,Kを作る。これを使って島1と島i、島iと島Nが結ばれているかどうかを判定する

島1と島iが結ばれていれば、リストJのi番目の要素をTrueに変更。島iと島Nが結ばれていれば、リストKのi番目の要素をTrueに変更。こうしてできた二つのリストを最初から見ていき、同じi番目がTrueを持っていれば、島iを経由して島1から島Nに行けると判断できる

 

ABC088C

実質連立方程式 変数の自由度が高ければ、1つ(ここではa_0)の値を0と仮定することで確かめることができたりする

 

ABC113C

11行目のところ

for k in range(M):
  if l == L[k][0]:
    cnt += 1
    L[k].append(cnt)

のcntとappendの位置が逆になっていたことが原因で昨日バグらせていた taiyoslime感謝します

d.hatena.ne.jp

リストLに対して、

L.sort()とすればリストの最初の要素でソートしてくれるけど、2番目の要素とかでソートしたいなーってなったら、

L = sorted(L, key = lambda x:x[1])

とすればよい 

 

 

2/17 AtCoder(エラトステネスの篩)

AtCoder

解くに至ったもの(4問)

ABC:117C/116C

みんプロ2019:D

全国統一プログラミング王決定戦:D

 

ABC117C

全く思いつかなかった

M個のチェックポイントにN個の駒を置いて、それらがチェックポイントをすべて移動しきるのにかかる経路の合計を最小にする問題

結局、N個の駒は移動することによりN個の区間を被覆することになる

実例を考えてみれば

1 2 3 4 5 6 7 8 9

を3つの駒で移動させることを考えると、例えば1,3,6に駒を置けば

[1,2],[3,4,5],[6,7,8,9]

と移動させることになり、区間は3つ

そうすると、考えるべきなのは、その区間にできるN-1個の境目の距離になる

例で言うと、[2,3]と[5,6]のような感じ

ここは、与えられたチェックポイントのうち、隣り合う2個のチェックポイントの距離にあたる(隣り合っているのは区間で被覆されているため)

そうすると、答えを出すうえで目指すべきなのは、全部は被覆するという前提を崩さず、いかにN-1個の境目の距離を大きくするかということになる

つまり、隣り合うチェックポイントの距離(N-1個)をソートして、上からN-1個取ってくる それを合計し、M個のチェックポイントのうち最も遠く離れた端と端の距離から引いたものが、求める駒の移動距離の最小値になる

 

解けなかったもの

ABC:084D/112C/113C

ABC084D

algoful.com

これを参考にして書いたコードがこちら

import math
L =
prime =

for i in range(2,100000):
  L.append(i)
M =
m = 0
while m+1 < math.sqrt(100000):
  m = L[0]
  prime.append(m)
  for i in L:
    if i%m == 0 :
      L.remove(i)

prime = prime+L
print(prime)

これだとTLEしてしまう 原因はremoveで、これは前から見ていって初めてその数字と同じになるやつを除くので、逐一検索していかないといけないため時間がかかる

(上のコードはせいぜい10^4までしか有用でない)

これを高速化する 参考にさせていただいたのはこちらのページ

juppy.hatenablog.com

#n以下の素数列挙(O(n log(n))
def primes(n):
  ass =
  is_prime = [True] * (n + 1)
  is_prime[0] = False
  is_prime[1] = False             ←ここまでで、素数判定のリストis_primeの要素の0,1番を         

             Falseにしたのと、ass(尻ではない)という、後に素数を

             入れる空のリストを作成した
  for i in range(2, int(n**0.5) + 1):
    if not is_prime[i]:              
      continue       ←ここでis_prime[i]がFalseならこれより下の文には進まな 

               い
    for j in range(i * 2, n + 1, i):  
       is_prime[j] = False  ←ここで、iの倍数をi*2からiの倍数ずつ見て、Falseにしてい 

             く
  for i in range(len(is_prime)):
    if is_prime[i]: 
      ass.append(i)                ←is_prime[i] == Trueであるiだけをリストassに追加していく
return ass

これで10^5でも十分速くなった ちなみにjuppyさんによるとdel,remove,insertは300点問題からだと実用性に欠けるとのこと 肝に銘じておきます

2/16 AtCoder(入力受け取りまとめ、数列の部分和、ユークリッドの互除法)/ss

AtCoder

解くに至った問題(15問)

キーエンスプログラミングコンテスト2019:A,B

エイジングプログラミングコンテスト2019:A,B

CADDi 2018 for Beginners:A

Tenka 1 Programmer Beginner Contest:2018A,B/2017B

CODE FESTIVAL 2016 Final:A

第5回 ドワンゴからの挑戦状 予選:A

AGC:023A/030A

ABC:058B/118A,B

 

listの中から未ACの100点問題が消えました わいわい 

 

CODE FES A

また入力で詰まった 一回入力を受け取る方法を整理しておいた方がよさそう

 思いつく限りでは、競プロC問題くらいまでで登場する入力のタイプは

① 'A'や'BC'といった文字列(一行、一列)

②1,23,456といった数字(一行、一列)

③'A BC'や'1 23'といったスペース区切りの文字列(一行、複数列)

④'A BC DEF......' や '1 2 3 4 5 .......'といったリストで受け取った方がいいもの(一行、複数列)

⑤1

 2

 3......という縦に並んだ入力(複数行、一列)

⑥1 2 3

 4 5 6

 7 8 9 といったような数字の行列(複数行、複数列)

⑦AB CD EF

 GH IJ KL

 MN OP QR といったような文字列の行列(複数行、複数列)

 

くらい(ほかにもありそうだけど) pythonでの受け取り方を整理

[3/1 ⑧模様の受け取りを追加しました]

 

 

 

① 'A'や'BC'といった文字列(一行、一列)

S = input()

 


②1,23,456といった数字(一行、一列)

N = int(input())

 


③'A BC'や'1 23'といったスペース区切りの文字列(一行、複数列)

文字列であれば、

S,T = input().split()

 

数字であれば、

N,M = map(int,input().split())

とする

 


④'A BC DEF......' や '1 2 3 4 5 .......'といった、スペース区切りで要素数が多いため、リストで受け取った方がいいもの(一行、複数列)

文字列であれば、

L = input().split() か L = list(input().split())         (L = [input().split()]でもよい)

が使える 出力はどちらも同じなので前者を推します

 

数字の列であれば

L = list(map(int,input().split()))

でよい

 


⑤1
 2
 3......という縦に並んだ入力(複数行、一列)

文字列であれば、数がすくなければ、 

S = input()

T = input()

......

と個々に受け取ることができる

数が大きくなった時は、リストに入れることを考えて、

L =
for i in range(行の数):
  L.append(input())

とするのがよい

数字はinput() を int(input())に変えればよい

 


⑥1 2 3
 4 5 6
 7 8 9 といったような数字の行列(複数行、複数列)

L =

for i in range(行数):

  L.append(list(map(int,input().split())))

とすれば、

L = [[1,2,3],[4,5,6],[7,8,9]]

となる

リストの要素のは、

1は L[0][0]、6はL[1][2]などとあらわされる

L.sort()とするとLの一番目の要素(L[j][0])で小さい順にソートされる


⑦AB CD EF
 GH IJ KL
 MN OP QR といったような文字列の行列(複数行、複数列)

L =

for i in range(行数):

  L.append(input().split())

 

これで、L = [['AB','CD',EF'],['GH','IJ','KL'],['MN','OP','QR']]

となる

 

#.#

.#.

#.# みたいに#と.でマスが塗ってあるみたいなのを要素ごとで受け取りたい

L =

for i in range(N):               #Nは行数

  L.append(list(input()))

とすれば、

L = [['#','.','#'],['.','#','.'],['#','.','#']]

と受け取ってくれる 

 

[注意]input().split()ってやると[[#.#],[.#.],[#.#]]と入ってしまう

 

 

 

AGC023A

数列の部分和で0になる箇所がいくつあるかを数える問題 わからなかったんだけど解説見て賢すぎてハゲ散らかした

 

第0項から各項までの和をリストに加えていく この中で等しいものを二つ選ぶと、0~i項と0~j項の差は0→i~j項という部分列の和は0になる 結局そこまでの和が同じものの中から二つ選んでくる場合の数の総和と同じになる 

 

かしこい かしこい かしこい むかつく むかつく むかつく

 

 

 

 

解けなかった問題

ABC118C

今日のABCのC問題 無限にバグらせて通らなかった

一番最初に提出したのは、リストをソートして最小のやつで割り切れなかったら1を、割り切れたらリストの最小を返すやつ 3つ通らないのがあり再検討 

のちに18,22,20,22みたいな組では2が答えになることから、リストの中の最小値と他を比べて最大公約数を持って来ればよいことに気づく、も時間が足りず惨敗

 

ユークリッドの互除法を使う x,yに対し最大公約数を求めるアルゴリズムは、

def gcd(x,y):

  while y != 0:

    k = x

    x = y

    y = k%y

  return  x

とする

今回は、隣り合う数の最大公約数をユークリッドの互除法アルゴリズムを用いて、候補と比較してより小さいものに更新し続け、最終的に最も小さいものを答えとして返せばよい

悔しい

2/15 AtCoder(文字列一致、階乗の約数の個数)/ss

AtCoder
解くに至ったもの(3問)
ARC:065C/066C/067C

 

ARC065C

daydream(かっこいい)

与えられた文字列がdream,dreamer,erase,eraserの4要素で構成されているか否かを判定する問題

愚直に前から見ていこうとすると、dreamまで見たとき、次に続く単語が'er'であれば、'dreamer'で終わってんのか、それとも'eraser'と続くのかが判断できなくて困る

ということで、文字列を逆から見ていく方針で行く

文字列を逆にして、maerd,remaerd,esare,resareで構成されているかを判断すればよい

これは、ある単語の終わりが別の単語の始まりと一致していないために採用できる

与えられた文字列sを逆にするには、

t = s[::-1]

とすればよい

 

ARC067C

思いつかなかった

階乗は math.factorial(N) でN!を求められる

カウントは初期値1、i = 2~1001で調べて、N!がiで割り切れる間は割り続け、カウントを増やす+N!をiで割るを繰り返し、割り切れなくなったらカウントを答えに掛ける、iを1増やす、カウントを1に戻す、を繰り返せばよい

1000程度であれば30msくらいで終わってくれる

 

解けなかったもの

ARC:072C/074C

ARC072C

リストの値を更新して、それまでの和が0にならない&前項までの和と異なるようにする問題

前から見てけばええやろと思って場合分けするも、最初が0スタートの場合とかの特殊な場合分けにまごついたり想定解と違うなどで結局ACできず

偶数番目の項までの和を奇数にするか偶数にするかで二通り分けてやるというのが答えだった また後でやります 

 

ARC074C

チョコを三つに分けてmax-minを最小にする問題 つまり三つに均等に分けようねという話 

横方向の長方形で三つに分けるのと、横方向×1縦方向×2でT字になるように分けるにパターンをやればよい そしてなるべく均等に分けるのがよいことも利用する

また後でやります 

 

今日は調子が悪かった 気分も沈んでました 明日は今日よりはやる

2/14 AtCoder(大文字小文字、円と四角形の内包、joinを使ったリスト内要素の区切り方、リストのコピー)/ss

AtCoder
解くに至ったもの(14問)
ABC:018A/072D/094D
ARC:045A/046A/047A/049A/050A/051A/053A/054A/055A/056A/080D

 

ARC050A

文字を大文字、小文字にする

大文字は

(文字列).upper()

小文字は

(文字列).lower() とすればよい

 

他にも、文字列すべての大文字小文字を逆にするには

(文字列).swapcase()

先頭一字のみ大文字にするには、

(文字列).capitalize() が使える

 

ARC051A

普通に解法が思いつかなかった 大丈夫か

円が長方形に内包されている→中心からxy方向それぞれに半径だけ伸ばした部分が長方形の四つの辺の内側にあるかどうかを調べる

長方形が円に内包されている→四つの(四つ。二つだとダメ)頂点が円の内側にあるかどうかを調べる(中心との距離を使う)

 

ARC080D

これも解法が思いつかなかった 色を盤面に塗り付けていく問題

色でタイルを塗る枚数が指定されていて、それぞれの色がその中で一筆書きできるようにするにはどうしたらいいかというもの

結局左上から蛇行するように右、左、右、左と進んでいけばよい 進んで色を塗り付けていくコードを書くのは簡単

問題は出力で、例えば

M = [[1,2,2,3,3],[4,4,4,4,3],[5,5,5,5,5]]を

1 2 2 3 3 

4 4 4 4 3

5 5 5 5 5

と出力するにはどうしたらよいか

こういう時は、

for j in range(3):
  print(' '.join(map(str,M[j])))

とする

printの中にはjoinという関数がある。これは、'?'.join(リスト)とすることで 

リストの要素(文字に限る)を?で区切った形で出してくれるというもの

例えば、

L = [a,bc,d]というリストを

a_bc_d

と出力したい場合、

print('_'.join(L))

とすればよい

 

んで、今回の場合、空白区切りで数字を出力したいということになる。数字は残念ながら扱えないので、一度文字列に直してから動かすという手法をとる

 

M = [[1,2,2,3,3],[4,4,4,4,3],[5,5,5,5,5]]を
1 2 2 3 3
4 4 4 4 3
5 5 5 5 5

と出力するには、

for j in range(3):

  print(' '.join(map(str,M[j])))

とする 

これは

①3回にわたって(改行して)リストに入れ子になっているリストを出すという発想を念頭に置いておき、printの部分で、

②' '.join は空白で区切って要素を出す

③リストの所に入っている map(str,M[j]) は、M[j]の要素を文字列に変換する 

という操作を行っている

 

この問題では使わなかったけど、リストのコピーをするときは

(新しいリスト) = copy.deepcopy(コピー元のリスト)

とするとよい

copy.copy(コピー元のリスト)というやり方もあるにはあるけど、これは新しく作ったリストの方で操作をすると、コピー元のリストもその変更が適用されてしまうという点で使い勝手がわるい

 

 

解けなかったもの

全国統一なんちゃら:C

ARC:091D

 

全国統一プログラミング王決定戦予選C

普通にわからず リストを用意して番号を前から1,2,3......って振ってコピー→A,Bの大きい順でソート→食べていった端からリストの番号に対応する要素を削除

とやろうとしたものの、ソートの時点でアホみたいに時間がかかりどのみちできなかった

解法は、高橋君青木君を別々に考えるのではなく、まずは青木君が全部食べてしまうと仮定して(鶴亀算っぽい)総和を

X = -(b_1+b_2+......b_n)

として、そこから高橋君が選択的にk個(これは偶奇による)食べていくと、そのたびにa_i+b_iが加算されるということから、結局a_i+b_iをソートして、大きい方からk個食べた(Xに加算した)と気がXを最大化できる、という風に考える

賢いね むかつく

 

ARC091D

 

わからず 数学ができない

bを固定して考える aをbでわった余りがK以上になるのをaが0~Nの範囲で求めればよい

N = pb+q (q<b) とあらわせるとすると、aを0~Nの範囲で動かしたとき、aをbで割った余りは 0,1,2......b-1というのがp回繰り返された後で(今のはa = 0 ~ bpをbで割った余り)、1,2......qで終了する

この中で余りがK以上になるのは、p回の繰り返しの中ではp*max(0,(b-K))個、残りの1~qではmax(0,(q-K-1))個ある  (maxで0との比較をしたのはbがKより小さくなる場合を除くため)

つまり、一般的なbに対して、条件を満たす組がp*max(0,(b-K))+max(0,(q-K-1))個存在することが分かったので、これをb=1からb=Nまで足し、最後にa = 0の場合を除けばよい

a = 0ではbで割った余りが0で、Kが0でなければ引く必要性はなく、Kが0の場合はp回余計にカウントされてしまっているので、求めた値からN*pを除けば(bが1~Nをカウントした分)よい

賢いね むかつく むかつく

 

 

 ABCのAを全部解き切りました めでたい

 

SS

エッチなのを投稿した Pixiv R-18ランキングで7位になり笑顔

2/13 AtCoder(文字列の置換、スライス、and、or、無限大)/ss

AtCoder
解くに至ったもの(16問)
ABC:039C/041C/043C/045C/048C
ARC:030A/032A/033A/034A/035A/036A/039A/040A/041A/043A/048A

 

ARC039A

文字列(数字)の一部を置換したいとき、例えば

A = 233

で、233を933に変えたいと思ったとき、

A[0] = '9'

はだめらしい('str' object does not support item assignment とエラーが出る。文字列は後から変更不可能とのこと)

書き換えたいときは、関数なりなんなりを用いて新しい文字列としてもう一度得る必要がある

だから、

C = A.replace(A[0],'9')

とすれば、C = 933と得られる。

嘘です ごめんなさい

A = 222だった場合、百の位だけ変えて232にしたいと思っても、

D = A.replace(A[0],'3')とすると、

D = 333となってしまう(A[0]自体は2であり、意味的には文字列中の2を全部3に置き換えることと同じになってしまうため)

これを避けるために、スライス(:)を使って、

D = A[:1] + '3' + A[2:]

とすると、D = 232となってくれる(n文字目を置き換える→A[:n-1]+'n文字目'+A[:n])

 

☆スライス

文字列sのn~n+k-1文字目の文字(つまりk個の文字)を取り出すときは、

t = s[n-1:n+k]

とすればよい。例えば1文字目から10文字取り出したいときは、

t = s[0:10]

とする

 

文字列の特定の場所だけ置換する方法を探してもなかなか見つかんなくて、代わりにreplaceを使うやつがたくさん出てきた 世の中の人は222を232にする必要性がないんだろうね 

 

 

and or

A = 991 ,B = 123に対して、A[1] != 9 and B[1] != 0 は偽である(A[1] == 9であるため)

 

この問題の場合、A[0]==9かつB[0]==1の場合とそれ以外......という風に分けていくと条件分岐が非常にめんどくさくて、上の真偽値も間違えやすい

→結局、A,Bの各桁を変える組合せを6パターン作り、各々を比較していくのが最も楽

 

ABC043C

無限大が欲しいときはfloat('inf')とすればよい               \\チカラガホシイカ......?//

 

解けなかったもの

ABC:044C(3次元配列とか出てきてわけわからんくなった)

ARC:029A/042A

2/12 AtCoder(入力の受け取り方、insert、set、count)/ss

AtCoder
解くに至ったもの(10問)
ABC:072C

ARC:019A/021A/022A/023A/024A/026A

みんプロ:B

全国統一なんちゃら:A,B

 

ABC072C

atcoder.jp

方針自体はすぐ決まったけどそれをちゃんとACさせるまで一時間 ヒェ

マズかったポイントは三つくらいあって、

①最初数字に0が入らないもんだと決めつけていた

②10^5のリストを作ろうとしてたけど①の勘違いにより実際は一つ足りなかった

③上二つに気づいたがリストMをつくるところでLに0を入れてwhile文を止めようとしていたため、Lが全部0の場合カウントされないことが起こっていた(このため2WAを食らう)

 ☆(リスト名).insert(n,a)

リストのn番目の文字の前にaを追加 だからリストの先頭に入れたいときはnを0にすればよい

 

ARC021A

与えられた数の集まりから行列みたいなのをつくるやつ

以前(2/10)学習したのはリスト内包表記だった 二日で忘れたのでもう一回書く 最悪

 

①リスト内包表記は、

L.append([int(i) for i in input().split()])

だった

②リスト内包表記を使わないと、

L = []

for i in range(行数):

  L.append(list(map(int(input().split()))

でイケる(要は各行で受け取った入力を並べてく、これ自体は前者と同じだけどこっちの方が直感的そう。ただリスト内包の方が実行時間早い?これはわかんない)

 

具体的には

2 3 3

4 1 0

2 4 5

みたいな入力に対し、

L = [[2,3,3],[4,1,0],[2,4,5]]

となる

数字じゃ無くても、L.append(input().split())とすれば文字のリストを得ることができる

 

ARC026A

リストで、重複する要素をまとめたいときは

set(リスト名)

とすればよい

しかし、これはリストと同じように「何番目の要素」という操作ができないので、

L = list(set(リスト名))

としてリストにする使い方をするのが便利そう

 

あと、リスト内の要素の数え方は

(リスト名).count(数えたいもの)

 

 

 

ss

えっちなのを3000字追加した

2/11 AtCoder(入力の受け取り方、int、floatの扱いについて)/ss

AtCoder

解くに至ったもの(15問)
ABC:040B,C
ARC:008A/009A/010A/011A/012A/014A/015A/016A/017A/018A/058A/073A
DPまとめコン:A

 

ARC009A

int型は切り捨て

 

ARC010A

昨日の続き的な

改行入力

①L = [input() for _ in range(回数)]

②L = []
for j in range(M):
  L.append([int(i) for i in input().split()])
print(L)

のどちらか好きな方を まあ①の方が楽 それはそう

 

☆②はsplit()をつけないと、例えば

10

20

30

という入力を受け取ったとき、[[1,0],[2,0],[3,0]]と入ってしまうので注意

 

スペース入力

10 20 30 を[10,20,30]と受け取ってほしくて、

L = input().split()とやると['10','20','30']と受け取られる。要素はあくまで文字列。

数値を要素にしたいときは、list(map(適用したい関数,リスト名))としてやればよい。

例えば、

L = list(map(int,input().split()))

などとすればL=[10,20,30]となる。(ここではリスト名の所にinput().split()を入れた。これが楽。)floatでもよい。

 

ARC012A

日曜か土曜で判定してほしくて

if D == 'Sunday' or  'Saturday'ってやったらダメだった

D == 'Sunday' or D == 'Saturday'としないとダメな模様

 

ARC018A

小数点はint()じゃなくてfloat()だった 完全に忘れてた

 

ARC058A

バチコリ時間かかった 5WA食らう インデントには気を付ける

 

ABC040B

思いつかなくて答えを見た 与えられた数nに対して、その中で作れる長方形の縦横の長さの絶対値abs(w-h)と、あまったタイルn-w*hの合計をできるだけ少なくする問題

全探索もどきが一番いいらしい 幅wを1~sqrt(n)、高さをw~n//wの範囲で取ってforで回す 計算量はn/1+n/2+......=nlognらしく、n <= 10^5なら間に合うとのこと すごいね

 

解けなかったもの

ABC:027B

ARC:013A

 

ABC027B

解決しました 原因は1==1.0が成り立つかどうかの理解が甘かったこと

atcoder.jp

12行目

if people//(c+1) != K

という記述、最初はif int(people//(c+1)) != Kと書いていたのですが、いずれにせよ切り捨てを行ったことで、例えば3 0 0と人数が与えられたときに2つ目の島を数えた時点で3//2==1→橋の本数が1本とカウントされてしまう というバグが発生していた模様

ここをダブルスラッシュではなくスラッシュに変えるとうまくいきました 結局1==1.0は成り立つというのが今回の結論 taiyoslimeありがとう

2/10 AtCoder(入力の受け取り方、replace)/ss

AtCoder

ABC:116A,B/117A,B

ARC:003A/004A/005A/006A/007A

みんプロ:A

 

 (スペース区切りの入力)

ARC004A

3
1 1
2 4
4 3 みたいな入力を受け取るところで詰まった スペースが入っているためmap(int,input().split())は厳しい

調べたらリスト内包型が便利とのこと

for文で回して(リスト名).append([int(i) for i in input().split()])とすれば[[1, 1],[2, 4],[4, 3]]というリストができる うれしいね うれしいよ


qiita.com

 

ARC005A

空白区切り(文字バージョン)をリストで入れたいときはlist(input().split())ではなくinput().split()とやれば勝手にリストにしてくれるっぽい

 

ARC007A

文字列中の余計な文字を削除する場合、(文字列).replace('何か','')とすればよい

当然(文字列).replace(何か,'また別の何か')にすれば「何か」→「また別の何か」になる

www.headboost.jp

 

こんな感じで十問解いた リハビリせんとね

 

ss

えっちなssを5000字くらい書いた

京都奈良 四日目

8時に起床  ホテル3階でご飯を食べる  3日目にお世話になったのはホテル日航奈良というところでした

f:id:saguh:20190209211254j:plain

9時過ぎに出発  奈良駅からJR奈良線に乗り宇治駅で下車  そこからタクシーを使い平等院

f:id:saguh:20190209211931j:plain

生の平等院鳳凰堂を見ました  すごい  空いていたのでついでに中も拝見しました  内部は改修工事の足場が組まれていた

お堂の中には阿弥陀如来とその周りに菩薩がいらっしゃる  寄木の天蓋は遠目から見てもめっちゃ細かくて感嘆しっぱなし  昔の人すごかったんやね  柱に残る絵の跡や壁画から平安の息吹を感じられました

ミュージアムの中を見て、池をぐるっと一周して入り口に戻ってくる  4日間古代の建造物や創作物に触れてきたわけですが、どれもこれも後世に語り継がれる価値ある素晴らしいもので、その価値はもちろん、これを創る人が沢山いたこと、その技術がそこまでちゃんと受け継がれてきたことに目頭が熱くなりました  創作する勇気を与えてもらった

藤棚があったので、藤の花が咲く季節にもう一度行ってみたいですね  絶対綺麗

 

それから平等院を出て、帰りは宇治駅まで歩き

途中で行きのタクシーの運転手さんにオススメしてもらった2件お茶屋さんに寄る

1軒目は稲房安兼というところ  観光客というよりかは地元の人がよく行く所という感じ  茶団子がお勧めということで買ってきた  安い  きな粉も何もかけないで食べたのですが甘さが控えめ、お茶の味がしっかり感じられるお団子  もっちりしてて美味しい

2軒目は中村藤吉本店というところ  まずは入り口入って奥のカフェみたいなところに入る

案内された席は一番奥で、窓から宇治川が見られるフォトジェニックなところ(といいつつ写真は撮っていない)  お薄とわらび餅を頂きました

f:id:saguh:20190209213801j:plainf:id:saguh:20190209213847j:plain

わらび餅なのですが蜜が2種類あります  僕は抹茶の方が好きでした  抹茶の粉かかってんのに抹茶かよとツッコミ入るかもしれないけどマジで美味しい  わらび餅自体はフワフワな食感  美味しい

お薄はお薄です()  昔別のお店で飲んだ濃茶のイメージが先行してバチバチに苦かったらどうしようかと心配してたけどそんなこともなく苦味も控えめで飲みやすかった  美味しい  抹茶羊羹も出てきたんだけどこっちは「アェッ」って変な声が出るくらいマジで美味しかった  ビックリした  食べるのが惜しいくらいでした

f:id:saguh:20190209214907j:plain

その後出口から出て横にあるお店の方へ  ここではカフェで提供されてるのが買えます  抹茶と抹茶羊羹、バウムクーヘンを買いました  抹茶は白と緑色とあるのですが白の方が安い  緑の方がお店で出ていたのですが 値段を聞いて30gで¥3,000らしくて卒倒した  オイオイオイオイ  通販サイトを覗いたら30g/¥10800とかいうのもあるのを見て世界の広さを知りました  知りたくなかった

そして徒歩で宇治駅へ  そこから京都駅に向かう

京都駅に着き、新幹線に乗る

f:id:saguh:20190209215024j:plain

無事に帰ってこれました  良かった  本当に楽しい3泊4日の旅でした

京都奈良 三日目

昨日ありえん早く出発して脚がボロボロになったので今日はゆっくり9時に出発  

再び奈良へ向かう  10時くらいに奈良駅側のホテルに  そこから法隆寺へと向かう

奈良駅から十分程度で法隆寺駅に着く  そこからタクシーを使い法隆寺

中心部から少しでも離れると外国人も少なくなるのか境内は閑散としていた

日本最古の木造建築は伊達ではなく京都の建物より侘びな感じ  五重塔とかは教科書で見るより実際見るとこう、なんとも言えない感じでした

f:id:saguh:20190209171725j:plain

屋根の瓦、柱に巻きつく木彫りの龍の造形、水が出てくる龍や鳳凰などなど、細部に渡って全く気を抜いてない  本当に驚いてばかりでした  これマジで1400~1300年前に出来たのか

釈迦三尊像は格子越しからしか見られんかったけど、神々しい顔の感じとか凄く印象的でした

宝物館ではいろんな時代に出来た仏の像がありました  作られ方も木で出来てたり、銅で出来てたり、塑像だったりと様々   すごい

玉虫厨子もありました  金属の彫り込みは細かいしその内側に玉虫の羽を使って装飾してたのを想像すると栄華極めてたんやろな〜〜〜〜とか思ったり

最後に夢殿をぐるっと巡って帰ってきました  木造建築色々見ましたがここはマジでピカイチ  素晴らしかった

f:id:saguh:20190209172248j:plain

 

それからバスで再び法隆寺駅へ......と思いきやバスの方向を完全に間違える  筒井駅というところで降りて大和西大寺近鉄奈良駅とリカバー

その後は昨日同様ならまちを散策しました  法隆寺が思いの外広くバチクソ疲れたので駅出てアーケード入ってすぐのすぐの寛永堂に駆け込みぜんざいを食べる

f:id:saguh:20190209172830j:plain

餅が二つ入ってます  いい感じに焦げてる  あったかくて甘い汁が体に沁みます  そして合間に塩昆布を食べるとこれがまた美味しい  かなり塩気がある分ぜんざいの甘さといい感じにマッチする  満足しました

その次は萬々堂通則  微笑を買いました  まだ食べてないのでアレですが和三盆の塊なので絶対美味しい  ついでにぶと饅頭と青丹よし(落雁)を買う  まだ食べてないので書きようがないですが美味しかった  美味しかったです()

その後中谷堂に行きました  寛永堂から行くとこっちの方が近いけど外国人観光客がめちゃくちゃ並んでたため一瞬様子を見てからにした  おっさん2人がありえん高速で餅を撞いてる実演も見ることができた  正月にニュースでよく見るアレですね((꜆꜄•̀ω•́)꜆꜄꜆  パックで三個入りを買ったが出来たてを買うべきだったよなと後悔  

中に餡子が入ってるよもぎ餅でした

f:id:saguh:20190209174110j:plain

 

最後に樫屋へ  ここでまた和菓子を食べる  入り口入ると上に上ってく箪笥でできた階段があるのですがありえん急  完全に若者でないと登れない仕様になっててウケました  でも店員さんこの階段上ってお盆を運んだりしてて強えなあと思いました

ここでもまたぜんざいを食べた  桐箱でおしぼりが出てきたりお茶の器を茶碗の柄がこちらを向くように回したりして色々オイオイオイと動揺するオタクになりました

ぜんざいは三種類の餅(ひえ、あわ、よもぎ)が入ってるのですが風味がそれぞれ違って面白い  よもぎはいつも食べるあの味ですがその他は完全に穀物感のある餅  そして餡子はめっちゃ甘い  甘く煮詰めている訳ではないらしい(蒸した小豆を甘い蜜につけただけ)のにこんなに甘くなるのかと驚き  粒も潰れてなくて凄いなあと感嘆しまくりながら一匙一匙涙を流して食べていた

f:id:saguh:20190209175110j:plain

くっついてきた煎餅は子種という名前だそう  煎餅表面に糖蜜が塗ってあるだけなのに香ばしい匂いと天にも昇る味でクソクソクソ美味かった  ビビる  

あと「みよしの」というぱっと見羊羹みたいな葛菓子を食べたけどこっちは上品な甘さ  絶対お茶に合う

f:id:saguh:20190209175519j:plain

それから奈良駅に歩いて帰ってくる  予報では関東が雪になるくらいだったのでここも外は死ぬほど寒く本当に凍死するかと思った  瀕死

だから外に繰り出して飯を食いに行く気にもなれず駅で済ませようということで駅一階にある三条坊といううどん屋で晩御飯を食べる  奈良だけのチェーン店っぽい  うどんは美味しかった  やはりうどんは神の食べ物

f:id:saguh:20190209175911j:plain

そんなこんなで三日目も終了  今日だけで一生分の甘いもの食べた気がする  とても幸せでした

 

京都奈良 二日目

7:00に起きる  とても眠い

とりあえず今日は奈良行ってみようという事で奈良へ

近鉄奈良駅で降りてそのまま歩いて東大寺f:id:saguh:20190208224225j:plain

早朝ということもあって人あんまおらんやろ〜〜〜〜とか舐めてたらそこそこいた  外国人観光客結構いっぱいいますね(まあオフシーズンなので日本人が少ないだけマシか)

 

境内はバカみたいに鹿が大量発生していた  奈良公園行って鹿と戯れようかな的なことを思ってたけど十分でした  というか鹿さん大人しいわね  俺なんかよりずっと人間に慣れてます  社会性で鹿にすら負ける大学生

 

めちゃくちゃ大きな大仏様を見たり、その周りの四天王の迫力を直に感じることができて良かった

 

くぐれる柱のところには修学旅行の中学生がわらわらいた  こんな季節でも修学旅行する学校あるのか

 

ミュージアムにも入ったのですがここは暖房効いてて椅子もあり、人もあんまり居なくてゆっくり出来ました  東大寺創建とか盧舎那仏の話とかのビデオが結構面白かった  創建に日本人口の半分が関わってたんですね  すごい  宝物も凄かったです  料金は別でかかりますが見る価値あり

 

そのあとは歩いて興福寺に戻りました  天平庵というところ  途中でご飯を軽く食べようとお店に寄る  釜飯(2度目)を食べました  和菓子がメインのお店なのか味は普通  実際に目の前で炊くタイプでした

f:id:saguh:20190208225646j:plain

 

興福寺に行きました  この辺り鹿がやたらいてそれに餌をやる外人がいるため治安が悪い  

 

まずは国宝館から  大きな千手観音像とか10人弟子、十二神将の像がありました  特に教科書とかで有名な阿修羅像が実際に見られたのはとても嬉しかった  

 

それから東金堂へ  寒い  中には薬師如来や菩薩が御坐す  荘厳

 

五重塔はメチャクチャ粋な建物です  よい

 

そして去年再建されたばかりの中金堂へ  鮮やかな色で塗られていかにも新築  回廊や門がないからなんか中途半端な感じはするけどそれでもかなり迫力がありました  人も少なく全容を見渡すことができた  中には釈迦如来像やその他の仏像がいっぱい

f:id:saguh:20190208231852j:plain

 

その後はならまちを散策  萬勝堂という和菓子屋さんにお邪魔させていただく  苺大福(¥230)はかねがね食べたいと思っていたので食べた  ありえん美味しい

f:id:saguh:20190208232311j:plain

 

お茶屋さんではないのでメニューはなかったけどお店の奥は座れるスペースがあり、そこで買ったお菓子を食べることができる  あったかいお茶も無料で飲めてとても素晴らしい  

 

苺大福は薄皮、餡子がたくさん入ってて、苺がめちゃくちゃ大きい  それにジューシーで甘酸っぱくて「これ、これだよ!俺が求めてた苺大福は!」と心の中で叫ぶ次第でございました  まーじで美味しかった  

 

それからまた歩いて春日大社へ  これが大誤算で、メチャクチャ遠くて登り坂+階段  道中で鹿と遊んでいる場合ではない  登ってから春日大社の周りをぐるっと回りました  御神木のサイドから生えてきた別の木が屋根ブチ破ってたり  このシーズン、夜は灯籠がライトアップされてて綺麗だそうですね  なんか昼でも申し訳程度に体験できるところ(暗い中に入っていって灯籠が灯ってるのを鑑賞できるゾーン)もあったり  とりあえず舐めない方がよかった

 

ヘロヘロになりバスで近鉄奈良駅まで戻ってくる

駅構内の「たまうさぎ」できな粉のお団子を買う

f:id:saguh:20190209000608j:plain

消費期限当日限り  きな粉の優しい味がほにゃほにゃという感じで美味しかったどす

 

京都に戻り、夜ご飯の場所へ  昨日はとてもお高いところだったので今日は方向性を変えて

 

天天有というラーメン屋に行きました  SSR塩見周子が行ってたのが大井町の天天有だそうなので、四条のお店に行って塩見周子のコスプレしてきました 天一ほどではないけどややこってり そしてほんのり甘みを感じる味 僕は好きです 壺に入った辛いタレはキムチ風味

f:id:saguh:20190209001039j:plain

そしてホテルに帰る  昨日今日と泊まったのは京都駅のグランヴィア京都というホテルでした  とてもよかった

 

f:id:saguh:20190209001221j:plain