---
# System prepended metadata

title: AtCoder Beginner Contest 317 題解
tags: [Algo, AtCoder]

---

# AtCoder Beginner Contest 317 題解

> 我好菜啊我好菜啊我好菜啊

Solve problem **A - E** by *python*

***

## [A - Potions (abc317 A)](https://atcoder.jp/contests/abc317/tasks/abc317_a)
### 題意

給定 $N$ 瓶藥水以及其可以恢復的生命值 $P_i$ 、當前生命值 $H$ 、目標生命值 $X$，求哪瓶藥水可以使生命回復到目標生命值或以上，並且這瓶藥水的功效要最小。

### 思路

對所有藥水做遍歷，保存符合條件的藥水。

:::spoiler 神奇的代碼
```python=
N, H, X = map(int, input().split(" "))
P = list(map(int, input().split(" ")))

ans = -1
for i, p in enumerate(P):
    if H + p == X:
        ans = i + 1
        break
    elif H + p > X:
        if ans == -1 or p < P[ans]:
            ans = i + 1
print(ans)
```
:::

***

## [B - MissingNo. (abc317 B)](https://atcoder.jp/contests/abc317/tasks/abc317_b)
### 題意

給定長度為 $N$ 的數列 $A$，其為長度為 $N+1$ 的連續數列的子集合，找出缺少的那個數為何。

### 思路

排序後由小到大遍歷所有元素，若當前元素不等於前一個元素+1，則此處就是缺少的數。

:::spoiler 神奇的代碼
```python=
N = int(input())
A = list(map(int, input().split(" ")))

A.sort()
for i in range(1, N):
    if A[i] - A[i-1] > 1:
        print(A[i-1]+1)
        break
```
:::

***

## [C - Remembering the Days (abc317 C)](https://atcoder.jp/contests/abc317/tasks/abc317_c)
### 題意

給定 $N$ 個點和 $M$ 條邊，以及每條邊對應的權重 $C_i$ ，求最長的路徑

### 限制

- $2 <= N <= 10$

### 思路：DFS

構建出相鄰矩陣後，對每個點當作起點做DFS即可，並且令DFS返回路徑的長度，在以N個點作為起點的最大路徑中，再取其中的最大值。

:::spoiler 神奇的代碼
```python=
N, M = map(int, input().split(" ")) 
edges = [list(map(int, input().split(" "))) for _ in range(M)]

if M == 1:
    print(edges[0][2])
    exit()

# 對每個點DFS，紀錄最大的距離
adj = [[0]*(N+1) for _ in range(N+1)]
for edge in edges:
    X, Y, W = edge
    adj[X][Y] = W
    adj[Y][X] = W

visited = [False for _ in range(N+1)]

ans = 0
def dfs(v):
    cur = 0
    visited[v] = True
    for j in range(1, N+1):
        if adj[v][j] != 0 and not visited[j]:
            res = dfs(j)
            cur = max(cur, res+adj[v][j])
    visited[v] = False
    return cur 

for i in range(1, N+1):
    ans = max(ans, dfs(i))
print(ans)
```
:::

***

## [D - President (abc317 D)](https://atcoder.jp/contests/abc317/tasks/abc317_d)
### 題意

給定 $N$ 個選區，以及每個選區中兩個候選人分別的得票數 $X_i$ 、$Y_i$、該選區的席次 $Z_i$，求若X要贏得一半以上的席次，需要從對手手中搶走多少票。

### 限制

- $1 \leq N \leq 100$
- $X_i+Y_i$ is odd.
- $\sum_{i=1}^N Z_i \leq 10^5$
- $\sum_{i=1}^N Z_i$ is odd.

### 思路：DP

- 令 $dp[i]$ 表示取得i個席位所需要的最少票數
- 令 $x$、$y$、$z$ 表示每個選區的兩位候選人的分別得票數，以及席次，計算需要搶走的票數 $vote$
- 可寫出轉移方程為 $dp[i] = min(dp[i], dp[i-z] + vote)$ 
- 在所有取得一半以上席位所需要的最少票數中，取最小值即為答案。

:::spoiler 神奇的代碼
```python=
N = int(input())  
# DP 
MAX_ZSUM = 10**5 # 所有席位的總數的邊界數量
# dp[i] 表示取得i個席位所需要的最少票數
dp = [0] + [float("inf")] * MAX_ZSUM

ZSUM = 0 # 記錄所有席位的總數
for _ in range(N):
  x,y,z = map(int,input().split())
  ZSUM += z 
  vote = max((y-x+1)//2, 0) # 贏下此選區所需要的票數
  for i in range(MAX_ZSUM-1, z-1, -1): # 由大到小更新，避免重複計算同一個選區
     dp[i] = min(dp[i], dp[i-z] + vote)

ans = min(dp[(ZSUM+1)//2:]) # 取得一半以上席位所需要的最少票數
print(ans)
```
:::

***

## [E - Avoid Eye Contact (abc317 E)](https://atcoder.jp/contests/abc317/tasks/abc317_e)
### 題意

給定一個迷宮 $Maze$，其中有若干個監視者，其朝向分別用`<>^v`表示，不能被監視者的視線看到，求能否從給定的起點走到終點，並且輸出路徑長度。

### 思路：用BFS走迷宮

- 首先先構建迷宮的矩陣，並在上下左右加上牆壁，以免出界
- 再來對迷宮矩陣做處理，確定哪些路徑可走
    - 先找出起點、終點、以及所有監視者的位置
    - 再來處理監視者，為了不影響對其他監視者的處理，依照題目所述，先標記為 $!$
    - 最後將地圖中的可走路徑標記為0，牆壁或可視為牆壁的符號標記為-1。
- 從起點開始做BFS，並且對可走路徑更新從起點走到此位置的最小距離
- 若找到終點或所有可走路徑都走完就終止，因為起點不等於終點，所以可以用走到終點的路徑長度是否為$0$來判斷能不能走到終點。

:::spoiler 神奇的代碼
```python=
# 構建迷宮的矩陣，並在上下左右加上牆壁，以免出界
H , W = map(int, input().split(" "))
Maze = [list(f"#{input()}#") for _ in range(H)]
Maze = [["#"]*(W+2)] + Maze + [["#"]*(W+2)]

# 先找出起點、終點、以及所有的箭頭位置
start = goal = None
arrows = []
for i in range(1, H+1):
    for j in range(1, W+1):
        if Maze[i][j] == "S":
            start = (i, j)
        elif Maze[i][j] == "G":
            goal = (i, j)
        elif Maze[i][j] in "<>^v":
            arrows.append((i, j))
# 再來處理箭頭
arrow_map = {"<":(0, -1), ">":(0, 1), "^":(-1, 0), "v":(1, 0)}
for pi, pj in arrows:
    di, dj = arrow_map[Maze[pi][pj]]
    i, j = pi+di, pj+dj
    while Maze[i][j] not in "#<>^v":
        Maze[i][j] = "!" # 為了不影響其他箭頭的判定情況，先標記為"!"
        i, j = i+di, j+dj
# 最後將地圖中的可走路徑標記為0，牆壁或可視為牆壁的符號標記為-1
# 這樣就可以用BFS來走迷宮了，且這個矩陣的元素代表路徑長度
for i in range(H+2):
    for j in range(W+2):
        if Maze[i][j] in "#!<>^v":
            Maze[i][j] = -1
        else:
            Maze[i][j] = 0
# BFS
from collections import deque
queue = deque([start])
while queue:
    pi, pj = queue.popleft()
    if (pi, pj) == goal: # 找到終點就停止
        break
    for di, dj in [(-1, 0), (1, 0), (0, -1), (0, 1)]: 
        if Maze[pi+di][pj+dj] == 0: #檢查上下左右是否可走且未走過
            Maze[pi+di][pj+dj] = Maze[pi][pj] + 1
            queue.append((pi+di, pj+dj))
print(-1) if Maze[goal[0]][goal[1]] == 0 else print(Maze[goal[0]][goal[1]])
```
:::