Pythonで螺旋本！螺旋本でPython！（〜１３章）

#※この記事は随時更新していきます。
#何の記事？
この記事は、螺旋本の問題を解いたものである。
言語はpythonを使用している。また一口メモも書いているので参考にして欲しい。

#chapter2

###Maximum Profit

最小値を保存することで、for文を回す手間を省けるよねという話。

```python
n = int(input())
price = []
for k in range(n):
    price.append(int(input()))

min_price = 1000000001
max_pro = -200000

for i in range(n):
    max_pro = max(max_pro, price[i]-min_price)
    min_price = min(price[i], min_price)

print(max_pro)
```
Chapter3

###insertion sort
jをwhileで回していく！

```python
arr = list(map(int, input().split()))

def insertion_sort(arr):
    for i in range(1,len(arr)):
        j = i-1
        v = arr[i]
        while j >= 0 and v < arr[j]:
            arr[j+1] = arr[j]
            j -= 1
        arr[j+1] = v
    print(arr)

insertion_sort(arr)
```

### bubble sort
changeでループ終了を判定する。

```python
arr = list(map(int, input().split()))

def bubble_sort(arr):
    change = True
    while change:
        change = False
        for i in range(1, len(arr))[::-1]:
            if arr[i] < arr[i-1]:
                arr[i], arr[i-1] = arr[i-1], arr[i]
                change = True
    return arr

print(bubble_sort(arr))
```
###selective sort
min()関数使ったらもっと楽です。

```python
arr = list(map(int, input().split()))

def selective_sort(arr):
    for i in range(len(arr)-1):
        min = arr[i]
        min_index = i
        for index in range(i+1,len(arr)):
            if arr[index] < min:
                min = arr[index]
                min_index = index
        arr[i], arr[min_index] = arr[min_index], arr[i]
    return arr

print(selective_sort(arr))

```
#Chapter4(データ構造）
###4.2 stack

```python
from collections import deque

Input = list(input().split())
stack = deque([])

for k in Input:
    if k not in ("+", "-", "*"):
        stack.append(int(k))
    elif k == "+":
        stack.append(stack.pop()+ stack.pop())
    elif k == "-":
        stack.append(-(stack.pop()- stack.pop()))
    elif k == "*":
        stack.append(stack.pop()*stack.pop())

print(stack.pop())

```
###4.3 Queue
```python
from collections import deque

n, q = map(int, input().split())
Queue = deque([])
for i in range(n):
    name, time = input().split()
    Queue.append([name, int(time)])

t = 0
while len(Queue) != 0:
    process = Queue.popleft()
    process[1] = process[1] - q
    if process[1] <= 0:
        t += q + process[1]
        print(process[0]+" "+str(t))
    else:
        t += q
        Queue.append(process)

'''
#入力
5 100
p1 150
p2 80
p3 200
p4 350
p5 20

＃出力
p2 180
p5 400
p1 450
p3 550
p4 800

'''
```

###4.6 Areas on the Cross-Section Diagram

```python
ground = input()
idx_stack = []

total_area = 0
for i, s in enumerate(ground):
    if s == "\\":
        idx_stack.append(i)
    elif s == "/" and idx_stack:
        j = idx_stack.pop()
        total_area += i-j

print(total_area)

'''
入力
\\///\_/\/\\\\/_/\\///__\\\_\\/_\/_/\

出力
35
'''
```
#Chapter5
###5.2 二分探索
二部探索はライブラリがあるのでそれ使うともっと楽チンです。
https://qiita.com/ta7uw/items/d6d8f0ddb215c3677cd3

```python
n = int(input())
s = list(map(int, input().split()))
q = int(input())
t = list(map(int, input().split()))

def binary_search(array, target):
    head = 0
    tail = len(array) -1
    while head <= tail:
        mid = (head+tail) // 2
        comp = array[mid]
        if comp > target:
            tail = mid - 1
        elif comp < target:
            head = mid + 1
        else:
            return True
    return False

ans = 0
for a in t:
    if binary_search(s,a):
        ans += 1
    else:
        continue

print(ans)
```

#Chapter6  (再帰・分割統治法）
###全探索

```python
n = int(input())
num = list(map(int, input().split()))
q = int(input())
q_num = list(map(int, input().split()))

'''
solve(i,j)はj番目までの数値でiを構成できるかどうかをTrueかFalseで返す関数
'''
'''
i, jの値を用いて再帰関数のループ終了条件を考える。
'''
def solve(i, j):
    if i == 0:
        return True
    if j < -1:
        return False
    return solve(i-num[j], j-1) or solve(i, j-1)


for k in q_num:
    if solve(k, n-1):
        print("Yes")
    else:
        print("No")

```
#Chapter7

###マージソート
indexが0の要素を引っ張ってくる必要があるから、listじゃなくてdequeを使って実装しました。
そしたら、dequeのスライスのスマートな実装が分からず、長くなってしまいました。
itertoolsのisliceを使いました。

[この方の記事](https://qiita.com/suecharo/items/30f5d817da4c948c3be6#%E3%83%9E%E3%83%BC%E3%82%B8%E3%82%BD%E3%83%BC%E3%83%88)がスマート

```python
from collections import deque
from itertools import islice

array = list(map(int,input().split()))
Q = deque(array)

def merge_sort(a):

    if len(a) == 1:
        return a
    else:
        mid = len(a) // 2
        return merge(merge_sort(deque(islice(a, 0, mid))), merge_sort(deque(islice(a, mid, len(a)))))

def merge(x, y):
    z = deque([])
    while len(x) > 0 and len(y) > 0:
        X = x[0]
        Y = y[0]
        if X < Y:
            z.append(x.popleft())
        elif Y < X:
            z.append(y.popleft())
        elif X == Y:
            z.append(x.popleft())
            z.append(y.popleft())

        if len(x) == 0 or len(y) == 0:
            z = z + x + y
    return z

print(list(merge_sort(Q)))
```

###クイックソート

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

def quick_sort(arr):

    if len(arr) <= 1:
        return arr

    #critの決め方はそれぞれ（今回は左端の数値を無条件でcritとしたがrandを使う場合もある）
    crit = arr[0]
    #critと同じ数値があった場合は、leftにもrightにも入れずに、カウントしてあとで付け加える・
    same_crit = 0

    left = []
    right = []
    for num in arr:
        if num < crit:
            left.append(num)
        elif num > crit:
            right.append(num)
        else:
            same_crit += 1
            
    left = quick_sort(left)
    right = quick_sort(right)

    return left + [crit] * same_crit + right

print(quick_sort(L))


```
###計数ソート

```python
array = list(map(int, input().split()))

def counting_sort(arr):

    max_num = max(arr)
    count = [0] * (max_num+1)
    sorted_arr = [0] * len(arr)

    for i in arr:
        count[i] += 1
    #累積和計算
    for k in range(max_num):
        count[k+1] = count[k+1] + count[k]

    for t in reversed(arr):
        i = count[t]
        #i-1にすることを忘れない
        sorted_arr[i-1] = t
        count[t] -= 1

    return sorted_arr

if __name__ == "__main__":
    print(counting_sort(array))
```
#chapter8
実装する機会がないと思うので省略します。
テキストを読み込んで二分木がどのような構造をしているのかを頭に入れていれば大丈夫だと思います。
必要性が出てきたらコード書きます。
#chapter9
同様の理由で省略。
二分木のノード削除の操作だけ少しめんどくさいので注意！
https://qiita.com/maebaru/items/a47c2ef675a76e8816ab
#Chapter10

###max-heap
heapqライブラリを使いましょう。
[こちらの記事](https://qiita.com/ell/items/fe52a9eb9499b7060ed6)がわかりやすかったです。

```python
from heapq import heapify, heappop, heappush

arr = list(map(int, input().split()))
#MAXheapの時は、-1をかける
arr = list(map(lambda x : x * (-1), arr))
heapify(arr)
heap_arr = [ s * (-1) for s in arr]
print(heap_arr)
```

###Priority_Queue

数列を入力して、max_heap表現をし、最大値を取り出す。

```python
from heapq import heapify, heappop, heappush

print("heapしたい数列を入力してください")
arr = list(map(int, input().split()))
arr = list(map(lambda x : x * (-1), arr))
heapify(arr)
heap_arr = [ s * (-1) for s in arr]
print("max_heap表現")
print(heap_arr)

print("挿入したい数を入力してください")
num = int(input())
arr = [ s * (-1) for s in heap_arr]
num = num * (-1)
heappush(arr, num)
heap_arr = [ s * (-1) for s in arr]
print("挿入後のmax_heap")
print(heap_arr)

print("最大値を取り出したい場合はYESを入力してください")
valid = input()
if valid == "YES":
    print((-1)* heappop(arr))
    heap_arr = [s * (-1) for s in arr]
    print("最大値を取り出された後の数列")
    print(heap_arr)



```


#Chapter11（動的計画法）

###11.4連鎖行列積

そもそも、実装するべき漸化式立てるのがむずい。
さらに、計算していく順番を考えるのもむずい。
計算していく順番は[こちら](https://aotamasaki.hatenablog.com/entry/2019/11/03/%E8%9E%BA%E6%97%8B%E6%9C%AC%E3%82%92Python%E3%81%A7%E8%A7%A3%E3%81%8F_Part2#P257-ALDS1_10_B-Matrix-Chain-Multiplication)を参考。
このfor文を回す順番を間違えるとうまくいかない！（考えれば当たり前だけど）


```python
n = int(input())
p = []
for t in range(n):
    a, b = map(int, input().split())
    if t == 0:
        p.append(a)
    p.append(b)

'''
dp[i][j]は、Mi~Mjを計算するための最小の掛け算の回数
'''
dp = [[float('inf')] * (n+1) for j in range(n+1)]
for k in range(n+1):
    dp[k][k] = 0
    dp[0][k] = 0
    dp[k][0] = 0

#lは対角成分からの距離
for l in range(1,n+1):
    for i in range(0,n-l+1):
        j = i + l
        for k in range(0,j-i):
            dp[i][j] = min(dp[i][j], dp[i][i+k] + dp[i+k+1][j] + p[i-1] * p[j] * p[i+k])
print(dp[1][n])


'''
入力
6
30 35
35 15
15 5
5 10
10 20
20 25

出力
15125
'''

```

#Chapter12

###深さ優先探索
螺旋本の問題より、Atcoderの問題が面白そうだったのでこちらをときました。（もはや記事の趣旨に反していますが...)
https://atcoder.jp/contests/atc001/tasks/dfs_a

```python

'''
https://atcoder.jp/contests/atc001/tasks/dfs_a
'''
import sys

h, w = map(int, input().split())
maze = [list(input()) for k in range(h)]

for i in range(h):
    for j in range(w):
        if maze[i][j] == "s":
            sx, sy = i, j
        elif maze[i][j] == "g":
            gx, gy = i,j

stack = [[sx, sy]]
visited = [[0] * w for i in range(h)]
visited[sx][sy] = 1

while stack:
    x, y = stack.pop()
    for s in [[1,0],[-1,0],[0,1],[0,-1]]:
        nx, ny = x+s[0], y+s[1]
        if 0 <= nx < h and 0 <= ny < w and visited[nx][ny] == 0 and maze[nx][ny] != "#":
            visited[nx][ny] = 1
            stack.append([nx, ny])
        if visited[gx][gy] == 1:
            print("Yes")
            sys.exit()
print("No")
```

###幅優先探索
https://atcoder.jp/contests/abc151/tasks/abc151_d

``` python
from collections import deque

h, w = map(int, input().split())
maze = [list(input()) for k in range(h)]

def bfs(start, goal):
    queue = deque([start])
    visited = [[0] * w for u in range(h)]

    while  len(queue) > 0:
        now_loc = queue.popleft()
        cx = now_loc[0]
        cy = now_loc[1]
        for d in [[1,0],[-1,0],[0,1],[0,-1]]:
            #nx, nyは次の位置
            #cx, cyは現在の位置
            nx = cx + d[0]
            ny = cy + d[1]

            if 0 <= nx < h and 0 <= ny < w and maze[nx][ny] != "#" and visited[nx][ny] == 0:
                visited[nx][ny] = visited[cx][cy] + 1
                queue.append([nx, ny])

        if visited[goal[0]][goal[1]] != 0:
            return visited[goal[0]][goal[1]]

    return 0

step = []
for i in range(h):
    for j in range(w):
        if maze[i][j] == "#":
            continue
        start = [i, j]
        for s in range(h):
            for t in range(w):
                if maze[s][t] == "#":
                    continue
                goal = [s,t]

                step.append(bfs(start, goal))

ans = max(step)
print(ans)
```
###連結成分

[こちらの記事](https://aotamasaki.hatenablog.com/entry/2019/11/03/%E8%9E%BA%E6%97%8B%E6%9C%AC%E3%82%92Python%E3%81%A7%E8%A7%A3%E3%81%8F_Part2#P287-ALDS1_11_D-Connected-Components)を参考にさせていただきました。
dfsの実装に、stackではなく再帰関数を用いています。

```python
from collections import defaultdict
import sys
sys.setrecursionlimit(25)

n, m = map(int, input().split())

#コネクションをdefaultdictを使って保存
connect = defaultdict(lambda: [])
for _ in range(m):
    s, t = map(int, input().split())
    connect[s].append(t)
    connect[t].append(s)

#各ノードの属するグループを辞書型で記録
group = {x:None for x in range(n)}

def dfs(node:int, gro:int):
    if group[node] is not None:
        return
    group[node] = gro

    for n in connect[node]:
        dfs(n, gro)

gro = 1
for node in connect.keys():
    dfs(node, gro)
    gro += 1

q = int(input())
ans = []
for _ in range(q):
    x, y = map(int, input().split())
    if group[x] == group[y]:
        ans.append("yes")
    else:
        ans.append("no")

for a in ans:
    print(a)
```
#Chapter13 重み付きグラフ

###最小全域木（プリム法）
こちらの方の[コード](https://juppy.hatenablog.com/entry/2019/02/18/%E8%9F%BB%E6%9C%AC_python_%E3%83%97%E3%83%A9%E3%82%A4%E3%82%AA%E3%83%AA%E3%83%86%E3%82%A3%E3%82%AD%E3%83%A5%E3%83%BC%28heapq%29%E3%82%92%E7%94%A8%E3%81%84%E3%81%9F%E3%83%97%E3%83%AA%E3%83%A0%E6%B3%95)を参考にさせていただきました。

また下のコードの入力は、螺旋本のように隣接行列を想定していません。
（入力の例は下のコードの最後をご覧ください）

[入力の形]
nodeの数 辺の数
node1 node2 cost
node3 node4 cost
.
.
.
の形です。



```python

from heapq import heappush, heappop


def prim_heap(connection):
    used = [False] * len(connection) #i番目のノードがすでに使われたかどうか
    node_heap = []
    for x in connection[0]:
        heappush(node_heap, x)
    used[0] = True
    ans = 0

    while len(node_heap) != 0:
        min_node = heappop(node_heap)
        if used[min_node[1]]:
            continue
        min_node_num = min_node[1]
        used[min_node_num] = True
        for y in connection[min_node_num]:
            if not used[y[1]]:
                heappush(node_heap, y)
        ans += min_node[0]

    return ans

n, v = map(int, input().split())
#[[[cost,node],[]...],[[],[].....],[]
s_connection = [[] for i in range(n)]
for i in range(v):
    s, t, u = map(int, input().split())
    s_connection[s].append([u, t])
    s_connection[t].append([u, s])


if __name__ == "__main__":
    print(prim_heap(s_connection))


'''

#入力
7 9
0 1 1
1 2 2
1 3 3
2 5 10
1 4 7
3 4 1
3 6 5
4 6 8
5 4 5

'''

```

###最短経路問題（ダイクストラ）
http://judge.u-aizu.ac.jp/onlinejudge/description.jsp?id=ALDS1_12_B&lang=jp



```python
from heapq import heappush, heappop, heappushpop, heapreplace, heapify

#隣接リスト作成
v = int(input())
edge_list = [[] for _ in range(v)]
for i in range(v):
    l = list(map(int, input().split()))
    for j in range(1,l[1]+1):
        edge_list[l[0]].append([l[2*j+1],l[2*j]])

def dijkstra(v, edges, start):
    d = [float("inf")] * v
    visited = [False] * v
    d[start] = 0
    visited[start] = 0
    hq = []
    for i in edges[start]:
        d[i[1]] = i[0]
        heappush(hq, i)

    while hq:
        x, y = heappop(hq)
        #もし更新する必要がないならこれ以降の処理を行わない
        if d[y] < x:
            continue
        d[y] = x
        visited[y] = True
        for n in edges[y]:
            if not visited[n[1]]:
                hq.append([x + n[0], n[1]])
        heapify(hq)
    return d

for index, d in enumerate(dijkstra(v, edge_list,0)):
    print(index,d)
```