プログラミングコンテスト・チャレンジブックのコードをPythonにしたメモ

マイナビのプログラミングコンテスト チャレンジブックでアルゴリズムを勉強中!

この本はC++でコードが書かれているので、勉強がてらPython3で書き直した時のメモ。

こちらの本とは微妙にアルゴリズムが違っている場合もあるかもしれないですが、悪しからず。


この本はとても勉強になるし、面白いのでぜひ読みましょう!(マイナビのまわし者ですww)

プログラミングコンテスト・チャレンジブック P24


# coding: utf-8
# Your code here!

#入力
L = 10
n = 3
list = [2,6,7]

def func():
    
    minTime = 0
    for i in range(0,n):
        minTime = max(minTime,min(list[i],L -list[i]))
        
    maxTime = 0
    for i in range(0,n):
        maxTime = max(maxTime,max(list[i],L -list[i]))
        
    print(maxTime,minTime)
    
func()


プログラミングコンテスト・チャレンジブック P26

本に書かれているコードの”l”と”1″のフォントがそっくりで、かなり迷ってしまった..2分探索を使用したアルゴリズム。


# coding: utf-8
# Your code here!

#入力
n = 5
m = 9
list = [3,0,5,5,5]

def binary_search(x):
    #xの存在し得る範囲はlist[l].list[l + 1],....list[r - 1]
    l = 0
    r = n
    
    while r - l >= 1:
        i = (l + r) / 2
        i = int(i)
        if list[i] == x:
            return True
        else:
            if list[i] < x:#xは後ろ半分の中にある
                l = i + 1
            else:#xは前半分のなかににある
                r = i
                
    return False


def solve():
    #2分探索を行うためにソート
    list.sort()
    
    f = False
    
    for a in range(0,n):
        for b in range(0,n):
            for c in range(0,n):
                if binary_search(m - list[a] - list[b] - list[c]):
                    f = True
    if f == True:
        print("Yes")
    else:
        print("No")
    
solve()

    



プログラミングコンテスト・チャレンジブック P30 階乗

n! ←nの階乗と言う意味。例えば3の階乗は3*2*1。1の階乗は1と定義されている。
関数の中でその関数を使用する再帰関数を使って階乗を計算する関数↓



# coding: utf-8
# Your code here!

def factorial(n):
    if n == 1:return 1
    return n*factorial(n-1)
        
print(factorial(10))
#3628800と出力

プログラミングコンテスト・チャレンジブック P30 フィボナッチ数列

※nが大きいとエラーが出る



def fibonacci(n):
    if n <= 1:return n
    return fibonacci(n-1) + fibonacci(n -2)
    
print(fibonacci())



プログラミングコンテスト・チャレンジブック P31 フィボナッチ数列 メモ探索

上記のコードより多少高速処理になるメモ探索。
※nが大きいとエラーが出る




# coding: utf-8
# Your code here!
memo = {}

def fibonacci(n):
    if n <= 1: return n
    if n not in memo:
        memo[n] = fibonacci(n-1) + fibonacci(n-2)
    return memo[n]

print(fibonacci(10)) 
#55と出力


プログラミングコンテスト・チャレンジブック P36 深さ優先探索



# coding: utf-8
# Your code here!
N = 10
M = 12

#空のフィールド(庭)を作成
field = []
for i in range(N):
    field.append(i)

field[0] = ["W",".",".",".",".",".",".",".",".","W","W","."]
field[1] = [".","W","W","W",".",".",".",".",".","W","W","W"]
field[2] = [".",".",".",".","W","W",".",".",".","W","W","."]
field[3] = [".",".",".",".",".",".",".",".",".","W","W","."]
field[4] = [".",".",".",".",".",".",".",".",".","W",".","."]
field[5] = [".",".","W",".",".",".",".",".",".","W",".","."]
field[6] = [".","W",".","W",".",".",".",".",".","W","W","."]
field[7] = ["W",".","W",".","W",".",".",".",".",".","W","."]
field[8] = [".","W",".","W",".",".",".",".",".",".","W","."]
field[9] = [".",".","W",".",".",".",".",".",".",".","W","."]


def dfs(x,y):
    if field[x][y] == "W":field[x][y] = "."
    
    for dx in range(-1,2):
        for dy in range(-1,2):
            nx = x + dx
            ny = y + dy
            
            if 0 <= nx and nx < N and 0 <= ny and ny < M and field[nx][ny] == "W":dfs(nx,ny)
    
    return



def solve():
    res = 0
    for i in range(N):
        for j in range(M):
            if field[i][j] == "W":
                dfs(i,j)
                res += 1
    print(res)
    
#実行
solve()
#3と出力    
            



プログラミングコンテスト・チャレンジブック P38 幅優先探索




# coding: utf-8
# Your code here!

N = 10
M = 10

INF = 100000000
#スタートの座標
sx = 0
sy = 1
#ゴールの座標
gx = 9
gy = 8
#各点までの最短距離の辞書
d = {}
for i in range(N):
    d[i] = {}

#空のフィールド(庭)を作成
maze = []
for i in range(N):
    maze.append(i)
#空のキューを作成    
que = []

maze[0] = ["#","S","#","#","#","#","#","#",".","#"]
maze[1] = [".",".",".",".",".",".","#",".",".","#"]
maze[2] = [".","#",".","#","#",".","#","#",".","#"]
maze[3] = [".","#",".",".",".",".",".",".",".","."]
maze[4] = ["#","#",".","#","#",".","#","#","#","#"]
maze[5] = [".",".",".",".","#",".",".",".",".","#"]
maze[6] = [".","#","#","#","#","#","#","#",".","#"]
maze[7] = [".",".",".",".","#",".",".",".",".","."]
maze[8] = [".","#","#","#","#",".","#","#","#","."]
maze[9] = [".",".",".",".","#",".",".",".","G","#"]

#移動4方向のベクトル
dx = [1,0,-1,0]
dy = [0,1,0,-1]

#(sx,sy)から(gx,gy)への最短距離を求める
#たどり着けないとINF
def bfs():
    #全ての点をINFで初期化
    for i in range(N):
        for j in range(M):
            d[i][j] = INF
            
    que.append([sx,sy])
    d[sx][sy] = 0
    
    
    while len(que):
        
        p = que[0]
        que.pop(0)
        
        if p[0] == gx and p[1] == gy:
            break
        
        #移動4方向をループ
        for i in range(4):
            #移動した後の点を(nx,ny)とする
            nx = p[0] + dx[i]
            ny = p[1] + dy[i]
            
            #移動が可能かの判定と既に訪れたことがあるかの判定
            if 0 <= nx < N and 0 <= ny < M and maze[nx][ny] != "#" and d[nx][ny] == INF:
                
                que.append([nx,ny])
                d[nx][ny] = d[p[0]][p[1]] + 1
                
                
    return d[gx][gy]           
    
print(bfs())
#22と出力


プログラミングコンテスト・チャレンジブック P43 貪欲法



# coding: utf-8
# Your code here!
#入力
V = [1,5,10,50,100,500]#硬貨の種類
c1 = 3
c5 = 2
c10 = 1
c50 = 3
c100 = 0
c500 = 2
C = [c1,c5,c10,c50,c100,c500] #硬貨の枚数
A = 620


def solve():
    ans = 0#コインの枚数カウント
    global A
    
    for i in range(5,-1,-1):
        t = min(int(A/V[i]),C[i])
        A -= t*V[i]
        ans += t
    print(ans)
        
solve()
#6と出力


プログラミングコンテスト・チャレンジブック P44 貪欲法,区間の問題



# coding: utf-8
# Your code here!

n = 5
s = [1,4,2,6,8]
t = [3,7,5,9,10]

#sとtをitvにまとめて終了時刻に早い仕事順に並び替え
itv = []
for i in range(len(s)):
    itv.append([t[i],s[i]])
    
itv = sorted(itv)

def solve():
    T = 0
    cnt = 0
    
    for i in range(len(itv)):
        if T < itv[i][1]:
            cnt += 1
            T = itv[i][0]
            
    print(cnt)
    
solve()
#3と出力

プログラミングコンテスト・チャレンジブック P47 辞書順



# coding: utf-8
# Your code here!

N = 6
S = "ABCBCD"

def solve():
    a = 0
    b = N -1#5
    
    while a <= b:
        #左から見た場合と右から見た場合を比較
        left = False;
        i = 0
        while a + i <= b:
            if S[a + i] < S[b -i]: left = True break elif S[a + i] > S[b -i]:
                left = False
                break
            i += 1
            
        if left:
            print(S[a],end="")
            a += 1
        else:
            print(S[b],end="")
            b -= 1

solve()
#ABCBCD


プログラミングコンテスト・チャレンジブック P48


# coding: utf-8
# Your code here!

N = 6
R = 10
X = [1,7,15,20,30,50]

def solve():
    
    global X
    X = sorted(X)#Xの順が昇順になっていなかった場合のための並び替え
    ans = 0
    i = 0
    
    while i < N:
        #sはカバーされていない一番左の点の位置
        s = X[i]
        #sから距離Rを超える点まで進む
        while i < N and X[i] <= s + R:
            i += 1
        #pは新しく印を付ける点の位置
        p = X[i -1]
        #pから距離Rを超える点まで進む  
        while i < N and X[i] <= p + R:
            i += 1
            
        ans += 1
        
    print(ans)
    
solve()

#3と出力

プログラミングコンテスト・チャレンジブック P50 貪欲法 二分木



# coding: utf-8
# Your code here!

L = [1,4,3,5,2]
N = len(L)

def solve():
    
    global N
    ans = 0
    #板が1本になるまで適応
    while N > 1:
        #一番短い板mii1,次に短い板mii2を求める
        mii1 = 0
        mii2 = 1
        if L[mii1] > L[mii2]:
            L[mii1],L[mii2] = L[mii2],L[mii1]#swap
        
        for i in range(2,N):
            if L[i] < L[mii1]:
                mii2 = mii1
                mii1 = i
            elif L[i] < L[mii2]:
                mii2 = i
        
        #それらを併合
        t = L[mii1] + L[mii2]
        ans += t
        
        if mii1 == N -1:
            L[mii1],L[mii2] = L[mii2],L[mii1]#swap
            
        L[mii1] = t
        L[mii2] = L[N - 1]
        N -= 1
            
    print(ans)
    
solve()
#33と出力

プログラミングコンテスト・チャレンジブック P53 動的計画法

今回はPHPで書いてみました。理由はPythonの辞書の取扱いに慣れていなかったのと、辞書の取扱いも一応参考書を読み直し、できたと思ったのですが、Pythonのエラーの読み方に慣れておらず、諦めてPHPで書くことにしました。
(PHPに訳した後に、Python3で書き直してみました。)
メモ(dp)を使い処理数を減らしています。下の事例では3回減っているようです。



// Your code here!
$n = 4;
$w = [2,1,3,2];
$v = [3,2,4,2];
$W = 5;



//空のリスト(メモ)
for($i = 0;$i < $n +1;++$i){
    for($j = 0;$j < $W +1;++$j){ 
     $dp[$i][$j] = -1;
   } 
} 

//$cnt = 0;//再帰処理回数のカウント 
//i番目以降の品物から重さの総和がj以下となるよう選ぶ 

function rec($i,$j){
global $dp; 
global $n; 
global $W; 
global $w; 
global $v; 
global $cnt; 

if($dp[$i][$j] >= 0){
        return $dp[$i][$j];
    }
    
    //++$cnt;
    //echo $cnt.PHP_EOL;
    //echo $i."-".$j.PHP_EOL;
    if($i == $n){
        #もう品物は残っていない
        $res = 0;
    }elseif($j < $w[$i]){
        #この品物は入らない
        $res = rec($i +1,$j);
    }else{
        #入れない場合と入れる場合を両方試す
        $res = max(rec($i +1,$j),rec($i +1,$j - $w[$i]) + $v[$i]);
    }
    //echo $res.PHP_EOL;
    return $dp[$i][$j] = $res;
}   

function solve(){
    global $W;
    print_r(rec(0,$W));
}

solve();
//7と出力


Python仕様↓



# coding: utf-8
# Your code here!
n = 4;
w = [2,1,3,2]
v = [3,2,4,2]
W = 5


dp = {}
#空のリスト(メモ)
for i in range(n +2):
    dp[i] = {}
    for j in range(W + 2):
        dp[i][j] = -1

#cnt = 0#処理回数のカウント       
#i番目以降の品物から重さの総和がj以下となるよう選ぶ      
def rec(i,j):
    
    if(dp[i][j] >= 0):
        return dp[i][j]
        
    #cnt += 1
    #print(cnt)
    #print('{0}-{1}'.format(i,j))
    
    if(i == n):
        #もう品物は残っていない
        res = 0
    elif(j < w[i]):
        #この品物は入らない
        res = rec(i +1,j)
    else:
        #入れない場合と入れる場合を両方試す
        res = max(rec(i +1,j),rec(i +1,j - w[i]) + v[i])
    
    #print(res)
    dp[i][j] = res
    return  res

def solve():
    global W
    print(rec(0,W))


solve()
#7と出力


プログラミングコンテスト・チャレンジブック P57 最長共通部分列



# coding: utf-8
# Your code here!
n = 4
m = 4
s = "abcd"
t = "becd"

dp = {}
for i in range(n +1):
    dp[i] = {}
    for j in range(m +1):
        dp[i][j] = 0
        
def solve():
    
    for i in range(n):
        for j in range(m):
            if s[i] == t[j]:
                dp[i +1][j + 1] = dp[i][j] + 1
            else:
                dp[i+ 1][j + 1] = max(dp[i][j + 1],dp[i + 1][j]);
            
    print(dp[n][m])
    
solve()
#3と出力


プログラミングコンテスト・チャレンジブック プライオリティキューを用いる問題P73

久しぶりにこの本を開いてpythonの復習、という理由により教科書とは少し違う風にしてしまった。。
Lの値が膨大な数だった場合は計算時間がかかってしまう。

さらにキューの使い方がよろしくないかもしれない、pythonのキューに関しては
参考サイトhttp://n-knuu.hatenablog.jp/entry/2015/05/30/183718



#!/usr/bin/env python
# -#- coding: utf-8 -#-
N = 4  #ガソリンスタンドの数
L = 25 #移動する距離
P = 10 #積まれているガソリンの量  1L/1P

A = [10,14,20,21]#スタートからのガソリンスタンドの位置
B = [10,5,2,4]#各スタンドで行える給油量

now = 0 #現在地
que = [] 
cnt = 0 #ガソリンスタンドのナンバー
ans = 0  #給油回数

#ゴールするまでループ
while  now < L:

  now += 1#1距離進む

  #スタンド通過したら給油できるガソリン量をキューに入れる
  if cnt < N: if now == A[cnt]: que.append(B[cnt]) cnt += 1 que.sort()#昇順に並び替え if P == 0:#ガソリンがなくなった if len(que) > 0:#現在地より以前に給油できるスタンドがあれば
          P += que.pop()#1番給油量が多く行えるスタンドで給油したことにする
          ans += 1#給油回数をカウント
      else:#途中に給油スタンドなし
          ans = -1#ゴールにたどり着けなかった場合の解答にしてループを抜ける
          break


  P -= 1#ガソリンを1消費

#解答
print(ans)



コメント