AtCoder ABC396 振り返り(緑コーダーがPythonでABCD問題)

## AtCoder ABC396 振り返り

今回はABCDまでの4問回答でした。レーティング的には微増になり、良い結果だったと思います。
E問題以降も解けそうな気もするのですけども、時間をかけて考えても結局は刃が立たないので、今後の課題です。

## [A - Triple Four](https://atcoder.jp/contests/abc396/tasks/abc396_a)

Aの先頭から、3連続で同じ文字があるかチェックしていきます。

```python
N = int(input())
A = list(map(int, input().split()))
for i in range(N-2):
    # 3連続で同じ数字があるかチェック
    if A[i] == A[i+1] == A[i+2]:
        print("Yes")
        exit()
print("No")
```

## [B - Card Pile](https://atcoder.jp/contests/abc396/tasks/abc396_b)

* 操作1 カードの山の一番上に積み重ねる
* 操作2 カードの山の一番上のカードを取り除く

という操作があるので、これはスタックを使うと良いとわかります。B問題でスタックを使う問題が出るのは珍しい感じがします。(配列で解いても良いかもしれません)

```python
Q = int(input())
que = deque() # 両面キューをスタックとして使う

# カードの山の初期化
for i in range(100):
    que.append(0)

for i in range(Q):
    query = list(map(int, input().split()))
    if query[0] == 1:
        # 操作1 カードの山の一番上に積み重ねる
        que.append(query[1])
    else:
        # 操作2 カードの山の一番上のカードを取り除き、出力
        p = que.pop()
        print(p)
```

## [C - Buy Balls](https://atcoder.jp/contests/abc396/tasks/abc396_c)

B、Wともに数字が大きいものから選んでいくと、より価値を得られることがわかります。そのため、B, Wを降順でソートしておくと良いです。

また、B, Wそれぞれの累積和を求めておくと、i個選んだときの最大の価値を計算量少なく求められます。

答えの出し方としては、まずBを何個選ぶか for ループで固定します。
B から i 個を選ぶ場合、Wを何個選べばいいかは、min(i, count_plus_w)個選ぶと最大の価値になります(count_plus_w は、Wの中でプラスの価値を持つ個数)。

```python
N, M = map(int, input().split())
B = list(map(int, input().split()))
W = list(map(int, input().split()))

# B, Wを降順にソート
B.sort(reverse=True)
W.sort(reverse=True)

# B, W の累積和を求める
ruiseki_b = list(itertools.accumulate(B))
ruiseki_w = list(itertools.accumulate(W))

# Wの中で0より大きい価値の個数を数える
count_plus_w = 0
for i in range(M):
    if W[i] > 0:
        count_plus_w += 1
    else:
        break

answer = 0
# Bをi(0からN)個選んだときの最大値を求める
for i in range(N):
    sum_b = ruiseki_b[i]
    sum_w = 0
    if count_plus_w == 0:
        answer = max(answer, sum_b)
        continue
    else:
        # Wは、min(i, count_plus_w)個選んだときが最大
        sum_w = ruiseki_w[min(i, count_plus_w - 1)]
        answer = max(answer, sum_b + sum_w)
    
print(answer)
```

## [D - Minimum XOR Path](https://atcoder.jp/contests/abc396/tasks/abc396_d)

以下の方針で解きました

手順1: 1からNまでのルートを、深さ優先探索で全て求めます。
手順2: その後、各ルートについてコストを計算して、最小値を求めました。

本来なら手順1、2を一緒にやってしまえば良いのですが、今回は N < 10 と頂点数が少ないので、多少無駄でもわかりやすく書くために分けました。

ただ、コードが長くなってしまい、逆に時間がかかってしまったかもしれません...。


```python
N, M = map(int, input().split())
path = defaultdict(list)
for i in range(M):
    u, v, w = input().split()
    u = int(u)
    v = int(v)
    path[u].append((v, w))
    path[v].append((u, w))

route = [] # 1からNまでのルートを格納する
current = 1

# 深さ優先探索で1からNまでのルートを求める
def dfs(v, visited_list):
    visited_list.append(v)
    # Nに着いたら、1からNまでのルートを格納
    if v == N:
        route.append(visited_list) 
        return

    # 次の頂点に進む
    for next in path[v]:        
        if next[0] not in visited_list:
            dfs(next[0], visited_list.copy())

dfs(1, [])

# ルートを与えると、コストを計算する
def calc_cost(route):
    if len(route) <= 1:
        return 0
    
    # コストだけを配列を作成する
    cost_array = []
    for i in range(len(route) - 1):
        current = route[i]
        nexts = path[current]
        for next in nexts:
            next_node = next[0]
            cost = int(next[1])
            if next_node == route[i + 1]:
                cost_array.append(cost)
        
    # 各コストのXORを取る
    cost = cost_array[0]
    for i in range(1, len(cost_array)):
        cost = cost ^ cost_array[i]
    return cost

# 各ルートのコストを計算し、最小のものを求める
answer = INF
for r in route:
    cost = calc_cost(r)
    answer = min(answer, cost)

print(answer)
```