# APCS實作題2022年1月第2題:贏家預測
> 日期:2023年9月22日
> 作者:王一哲
> 題目來源:111年1月實作題第2題
> [ZeroJudge 題目連結](https://zerojudge.tw/ShowProblem?problemid=h082)
<br />
## 題目
### 問題描述
有 $n$ 的人要比賽,每個人的戰力為 $S[1], S[2], \dots, S[n]$,而應變力為 $T[1], T[2], \dots, T[n]$,編號為 $1$ 到 $n$。
一開始將這 $n$ 個人按照編號為 $idx[1], idx[2], \dots, idx[n]$ 的順序排成一列,並從陣列前端開始兩兩一組進行配對競賽,若該 round 有奇數人數,則最後一個烙單的人直接晉級下一 round,並不獲得戰力和應變力數值的增加。
每一場競賽的勝負判斷規則如下,假設第一個人的戰力為 $a$,應變力為 $b$,第二個人的戰力為 $c$,應變力為 $d$。
1. 若 $ab \geq cd$ 則第一個人獲勝,並且勝利方(第一個人)的戰力變為 $a + cd/(2b)$,應變力變為 $b + cd/(2a)$,失敗方(第二個人)的戰力變為 $c + c/2$,應變力變為 $d + d/2$。
2. 若 $ab < cd$ 則第二個人獲勝,並且勝利方(第二個人)的戰力變為 $c + ab/(2d)$,應變力變為 $d + ab/(2c)$,失敗方(第一個人)的戰力變為 $a + a/2$,應變力變為 $b + b/2$。
以上除法皆為無條件捨去。
該 round 每組勝負揭曉後,按照原有順序將他們排列並分成勝利組和失敗組,失敗組中若有人已經輸了 $m$ 次則被淘汰,再將失敗組接在勝利組之後形成新的排列進行下一 round,直到僅剩一個人成為最終勝利者,並輸出他的編號。
<br />
### 輸入說明
第一行輸入兩個正整數 $n$ 和 $m$,接下來一行有 $n$ 個正整數代表每個人分別的戰力值,接下來一行有 $n$ 個正整數代表每個人分別的應變力值,最後一行有 $n$ 個正整數代表第一 round 的初始排列順序。計算過程中 $ab$ 相乘的數值可能超過 $2^{23}$,但保證數值不超過 $2^{60}$。
數字範圍
- $2 \leq n \leq 1000$, $1 \leq m \leq 5$
- $1 \leq S[i], T[i] \leq 100$
子題配分
- 50%:$2 \leq n \leq 100$, $m = 1$
- 50%:無額外限制
<br />
### 輸出說明
輸出最終贏家的編號。
<br />
### 範例一:輸入
```
4 1
4 2 5 3
2 5 1 5
1 2 3 4
```
### 範例一:正確輸出
```
4
```
<br />
### 範例二:輸入
```
4 5
4 1 5 3
6 5 1 6
4 1 3 2
```
### 範例二:正確輸出
```
1
```
<br />
## 提示
範例輸入 1
第 1 輪的編號為:{1, 2, 3, 4}, 戰力為:{4, 2, 5, 3}, 應變力為:{2, 5, 1, 5}, 失敗次數為:{1, 0, 1, 0}
第 2 輪的編號為:{2, 4}, 戰力為:{6, 2, 7, 3}, 應變力為:{3, 7, 1, 5}, 失敗次數為:{1, 1, 1, 0}
範例輸入 2
第 1 輪的編號為:{4, 1, 3, 2}, 戰力為:{4, 1, 5, 3}, 應變力為:{6, 5, 1, 6}, 失敗次數為:{0, 1, 0, 1}
第 2 輪的編號為:{1, 3, 4, 2}, 戰力為:{5, 1, 7, 4}, 應變力為:{8, 7, 1, 9}, 失敗次數為:{0, 2, 1, 1}
第 3 輪的編號為:{1, 4, 3, 2}, 戰力為:{5, 1, 10, 4}, 應變力為:{8, 10, 1, 9}, 失敗次數為:{0, 3, 1, 2}
第 4 輪的編號為:{1, 3, 4, 2}, 戰力為:{7, 1, 15, 6}, 應變力為:{11, 15, 1, 13}, 失敗次數為:{0, 4, 2, 2}
第 5 輪的編號為:{1, 4, 3, 2}, 戰力為:{7, 1, 22, 6}, 應變力為:{12, 22, 1, 14}, 失敗次數為:{0, 5, 2, 3}
第 6 輪的編號為:{1, 3, 4}, 戰力為:{10, 1, 33, 9}, 應變力為:{18, 33, 1, 21}, 失敗次數為:{0, 5, 3, 3}
第 7 輪的編號為:{1, 4, 3}, 戰力為:{10, 1, 49, 9}, 應變力為:{19, 33, 1, 21}, 失敗次數為:{0, 5, 3, 4}
第 8 輪的編號為:{1, 3, 4}, 戰力為:{14, 1, 49, 13}, 應變力為:{28, 33, 1, 31}, 失敗次數為:{0, 5, 4, 4}
第 9 輪的編號為:{1, 4, 3}, 戰力為:{14, 1, 73, 13}, 應變力為:{29, 33, 1, 31}, 失敗次數為:{0, 5, 4, 5}
第 10 輪的編號為:{1, 3}, 戰力為:{20, 1, 73, 19}, 應變力為:{43, 33, 1, 46}, 失敗次數為:{0, 5, 5, 5}
## Python 程式碼
於 ZeroJudge 測試結果,最長運算時間約為 41 ms,使用記憶體最多約為 3.6 MB,通過測試。
```python=
n, m = map(int, input().split()) # 人數、失敗次數上限
S = list(map(int, input().split())) # 戰力
T = list(map(int, input().split())) # 應變力
Q = list(map(int, input().split())) # 上場順序
M = [0]*n # 每人失敗次數
while len(Q) > 1: # Q 長度大於1繼續執行
#print("Q:", Q, " S:", S, "T:", T, end=" ") # 除錯用
W, L = [], [] # 贏家、輸家
while Q: # Q 如果有內容繼續執行
if len(Q) == 1: # 如果這輪只剩下1人
W.append(Q.pop(0)) # 直接加到贏家名單
else: # 如果這輪還有2人以上
q1 = Q.pop(0) # 取出 Q 最前面的人存入 q1
q2 = Q.pop(0) # 取出 Q 最前面的人存入 q2
a, b, c, d =S[q1-1], T[q1-1], S[q2-1], T[q2-1] # 取出兩人對應的戰力及應變力,分別存入a, b, c, d
if a*b >= c*d: # 如果 q1 獲勝
W.append(q1) # 將 q1 加到贏家名單
S[q1-1] = a + c*d//(2*b) # 更新 q1 對應的戰力
T[q1-1] = b + c*d//(2*a) # 更新 q1 對應的應變力
S[q2-1] = c + c//2 # 更新 q2 對應的戰力
T[q2-1] = d + d//2 # 更新 q2 對應的應變力
M[q2-1] += 1 # q2 對應的失敗次數加1
if M[q2-1] < m:L.append(q2) # 如果 q2 失敗次數小於 m,將 q2 加入輸家名單
else: # 如果 q2 獲勝
W.append(q2) # 將 q2 加到贏家名單
S[q1-1] = a + a//2 # 更新 q1 對應的戰力
T[q1-1] = b + b//2 # 更新 q1 對應的應變力
S[q2-1] = c + a*b//(2*d) # 更新 q2 對應的戰力
T[q2-1] = d + a*b//(2*c) # 更新 q2 對應的應變力
M[q1-1] += 1 # q1 對應的失敗次數加1
if M[q1-1] < m:L.append(q1) # 如果 q1 失敗次數小於 m,將 q1 加入輸家名單
#print("M:", M) # 除錯用
Q = W + L # 更新下一輪的名單
print(Q[0]) # 印出答案
```
<br /><br />
## C++ 程式碼
於 ZeroJudge 測試結果,最長運算時間約為 4 ms,使用記憶體最多約為 360 kB,通過測試。
```cpp=
#include <iostream>
#include <queue>
using namespace std;
int main() {
int n, m; cin >> n >> m; // 人數、失敗次數上限
unsigned long long S[n], T[n]; // 戰力、應變力
int M[n] = {0}; // 每人失敗次數
queue<int> Q; // 上場順序
for(int i=0; i<n; i++) cin >> S[i];
for(int i=0; i<n; i++) cin >> T[i];
for(int i=0; i<n; i++) {
int tmp; cin >> tmp;
Q.push(tmp);
}
while(Q.size() > 1) { // Q 長度大於1繼續執行
queue<int> W, L; // 贏家、輸家
while(!Q.empty()) { // Q 如果有內容繼續執行
if (Q.size() == 1) { // 如果這輪只剩下1人
W.push(Q.front()); // 直接加到贏家名單
Q.pop();
} else { // 如果這輪還有2人以上
int q1 = Q.front(); Q.pop(); // 取出 Q 最前面的人存入 q1
int q2 = Q.front(); Q.pop(); // 取出 Q 最前面的人存入 q2
unsigned long long a = S[q1-1], b = T[q1-1], c = S[q2-1], d = T[q2-1]; // 取出兩人對應的戰力及應變力,分別存入a, b, c, d
if (a*b >= c*d) { // 如果 q1 獲勝
W.push(q1); // 將 q1 加到贏家名單
S[q1-1] = a + c*d/(2*b); // 更新 q1 對應的戰力
T[q1-1] = b + c*d/(2*a); // 更新 q1 對應的應變力
S[q2-1] = c + c/2; // 更新 q2 對應的戰力
T[q2-1] = d + d/2; // 更新 q2 對應的應變力
M[q2-1]++; // q2 對應的失敗次數加1
if (M[q2-1] < m) L.push(q2); // 如果 q2 失敗次數小於 m,將 q2 加入輸家名單
} else { // 如果 q2 獲勝
W.push(q2); // 將 q2 加到贏家名單
S[q1-1] = a + a/2; // 更新 q1 對應的戰力
T[q1-1] = b + b/2; // 更新 q1 對應的應變力
S[q2-1] = c + a*b/(2*d); // 更新 q2 對應的戰力
T[q2-1] = d + a*b/(2*c); // 更新 q2 對應的應變力
M[q1-1]++; // q1 對應的失敗次數加1
if (M[q1-1] < m) L.push(q1); // 如果 q1 失敗次數小於 m,將 q1 加入輸家名單
}
}
}
/* 更新下一輪的名單 */
while(!W.empty()) {
Q.push(W.front()); W.pop();
}
while(!L.empty()) {
Q.push(L.front()); L.pop();
}
}
cout << Q.front() << endl; // 印出答案
return 0;
}
```
<br /><br />
---
###### tags:`APCS`、`Python`、`C++`