#P2895. 第2题-游戏中最后玩家的生命值
          
                        
                                    
                      
        
              - 
          
          
                      1000ms
            
          
                      Tried: 690
            Accepted: 147
            Difficulty: 4
            
          
          
          
                       所属公司 : 
                              华为
                                
            
                        
              时间 :2025年4月23日-暑期实习(留学生)
                              
                      
          
 
- 
                        算法标签>优先队列          
 
第2题-游戏中最后玩家的生命值
题解思路
该问题要求模拟一系列“玩家1对1对战”的过程,每轮选择当前生命值最小且不为0的两名玩家进行PK,直到无法匹配出两名玩家或所有玩家生命值归零。
显然,每轮都要快速地找到当前生命值最小的两名玩家,因此可以使用 优先队列(最小堆) 来维护玩家集合。
算法步骤
- 初始化
- 将编号和生命值均大于0的玩家以 
(life, id)为键插入最小堆。 
 - 将编号和生命值均大于0的玩家以 
 - 循环PK
- 当堆中元素少于2时,终止循环。
 - 弹出堆顶的两名玩家 
(life1, id1)和(life2, id2)。 - 若 
life1 == life2,则两人同归于尽,直接丢弃。 - 否则,胜者为生命值较大的那位(设 
life_big, id_big),剩余生命值rem = life_big - life_smaint,计算新生命值newLife = rem * 3,并将(newLife, id_big)重新插入堆中。 
 - 输出结果
- 循环结束后,若堆为空,则输出 
-1; - 否则堆中仅剩一位玩家,弹出并输出其编号和生命值。
 
 - 循环结束后,若堆为空,则输出 
 
相关算法
- 优先队列(Priority Queue)
用于动态维护一个集合,支持在 O(logN) 时间内的插入与删除最小元素操作。 
复杂度分析
- 初始化建堆需要 O(N)(或 O(NlogN) 若逐个插入);
 - 每轮PK涉及两次堆弹出和一次可能的堆插入,共 O(logN);
 - 最多进行 N−1 次PK,故总时间复杂度为 O(NlogN)
 - 空间复杂度主要用于存储堆,最多 O(N)。
 
代码实现
Python
import sys
import heapq
MAX_LIFE = 2**31 - 1
def solve():
    input = sys.stdin.readline
    n = int(input().strip())
    lives = list(map(int, input().split()))
    
    # 最小堆,元素为 (life, id)
    heap = []
    for i, v in enumerate(lives, start=1):
        if v > 0:
            heapq.heappush(heap, (v, i))
    
    while len(heap) >= 2:
        life1, id1 = heapq.heappop(heap)
        life2, id2 = heapq.heappop(heap)
        if life1 == life2:
            # 同归于尽
            continue
        # 分出胜负
        if life1 > life2:
            life_big, id_big, life_smaint = life1, id1, life2
        else:
            life_big, id_big, life_smaint = life2, id2, life1
        rem = life_big - life_smaint
        new_life = rem * 3
        if new_life > MAX_LIFE:
            new_life = MAX_LIFE
        if new_life > 0:
            heapq.heappush(heap, (new_life, id_big))
    
    # 输出
    if not heap:
        print(-1)
    else:
        life, pid = heap[0]
        print(pid, life)
if __name__ == "__main__":
    solve()
Java
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.util.PriorityQueue;
import java.util.StringTokenizer;
public class Main {
    private static final int MAX_LIFE = Integer.MAX_VALUE;
    static class Player implements Comparable<Player> {
        int life, id;
        Player(int l, int i) { life = l; id = i; }
        @Override
        public int compareTo(Player o) {
            // 先按生命值升序,再按 id 升序
            if (life != o.life) return Integer.compare(life, o.life);
            return Integer.compare(id, o.id);
        }
    }
    public static void main(String[] args) throws Exception {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        int n = Integer.parseInt(br.readLine().trim());
        StringTokenizer st = new StringTokenizer(br.readLine());
        PriorityQueue<Player> pq = new PriorityQueue<>();
        // 1. 初始化:只将生命值>0的玩家入堆
        for (int i = 1; i <= n; i++) {
            int v = Integer.parseInt(st.nextToken());
            if (v > 0) {
                pq.offer(new Player(v, i));
            }
        }
        // 2. 循环 PK
        while (pq.size() >= 2) {
            Player p1 = pq.poll();
            Player p2 = pq.poll();
            if (p1.life == p2.life) {
                // 同归于尽,直接丢弃
                continue;
            }
            // 胜者和败者
            Player win = p1.life > p2.life ? p1 : p2;
            Player lose = p1.life > p2.life ? p2 : p1;
            long rem = (long)win.life - lose.life;      // 防溢出用 long
            long nl = rem * 3;
            if (nl > MAX_LIFE) nl = MAX_LIFE;           // clamp
            if (nl > 0) {
                pq.offer(new Player((int)nl, win.id));
            }
        }
        // 3. 输出
        if (pq.isEmpty()) {
            System.out.println(-1);
        } else {
            Player ans = pq.peek();
            System.out.println(ans.id + " " + ans.life);
        }
    }
}
C++
#include <bits/stdc++.h>
using namespace std;
static const int MAX_LIFE = INT_MAX;
struct Player {
    int life;
    int id;
};
struct Cmp {
    bool operator()(const Player &a, const Player &b) const {
        // 小顶堆:生命值小的排在前,若相等按 id
        if (a.life != b.life) return a.life > b.life;
        return a.id > b.id;
    }
};
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n;
    cin >> n;
    priority_queue<Player, vector<Player>, Cmp> pq;
    // 1. 初始化
    for (int i = 1; i <= n; ++i) {
        int v;
        cin >> v;
        if (v > 0) {
            pq.push({v, i});
        }
    }
    // 2. 循环 PK
    while (pq.size() >= 2) {
        Player p1 = pq.top(); pq.pop();
        Player p2 = pq.top(); pq.pop();
        if (p1.life == p2.life) {
            // 同归于尽
            continue;
        }
        // 胜负判定
        Player win = (p1.life > p2.life ? p1 : p2);
        Player lose = (p1.life > p2.life ? p2 : p1);
        long rem = (long)win.life - lose.life;
        long nl = rem * 3;
        if (nl > MAX_LIFE) nl = MAX_LIFE;   // clamp
        if (nl > 0) {
            pq.push({(int)nl, win.id});
        }
    }
    // 3. 输出
    if (pq.empty()) {
        cout << -1 << "\n";
    } else {
        Player ans = pq.top();
        cout << ans.id << " " << ans.life << "\n";
    }
    return 0;
}
        题目内容
有一款游戏,里面有N个游戏玩家 (1<=N<100000),每个玩家都有一个初始的生命能量值lifePower(0<=lifePower<=231−1)
游戏规则:经过多玩家1对1 PK 直到游戏结束,每一轮PK都是从游戏中尚存的所有玩家中挑选生命能量值最小且不为0的2个玩家进行PK(如果在挑选符合PK条件的2个玩家过程中,能量值相同的候选玩家有多个时,则挑选玩家编号小的玩家参与PK),如是两个玩家的生命能量值相同,则2个玩家同归于尽,生命能量值都归为0,PK后都退出游戏;如果2个玩家的生命能量值不同,则能量值大的玩家胜出,PK过程胜出玩家会消耗掉另一个玩家的同等数量的生命能量值,但是PK胜出后,则剩余生命能量值会膨胀3倍(最大不超过32位有符号整数的最大值即 231−1),即为该玩家最终的生命能量值为剩余生分能量值的3倍。PK胜出的玩家仍可继续游戏;
如果游戏无法挑选出2个符合条件的玩家进行PK时,游戏结束;
请设计一个高效的算法,让游戏按规则进行玩家PK,直到游戏结束。
输入描述
第一行为游戏玩家的数量,取值范围:1<=N<100000
第二行为空格间隔的 N 个游戏玩家的生命能量值 lifePower,按照玩家编号从1到N排列。取值范围:0<=lifePower<231−1
输出描述
打印最后PK胜出玩家的编号和该玩家的最终生命能量值,以1个空格为间隔。如果游戏经过PK后,没有留存下来生命能量值大于0的玩家,则打印−1。
样例1
输入
4
2 5 1 8
输出
4 6
说明
第1轮PK,最小的2个玩家是玩家1和玩家3,生命能力值分别是2和1,玩家1胜出,剩余能量值为1,膨胀3倍后最终生命能量值为3
第1轮PK后所有玩家的生命能量值为:3 5 0 8
第2轮PK,最小的2个玩家是玩家1和玩家2,生命能力值分别是3和5,玩家2胜出,剩余能量值为2,膨胀3倍后最终生命能量值为6
第2轮PK后所有玩家的生命能量值为:0 6 0 8
第3轮PK,最小的2个玩家是玩家2和玩家4(也是最后2个玩家),生命能量值分别是6和8,玩家4胜出,剩余能量值为2,膨胀3倍后最终生命能量值为6
第3轮PK后所有玩家的生命能量值为:0 0 0 6
返回 4 6
样例2
输入
3
2 5 9
输出
-1
说明
第1轮PK,最小的2个玩家是玩家1和玩家2,生命能力值分别是2和5,玩家2胜出,剩余能量值为3,膨胀3倍后最终生命能量值为9
第1轮PK后所有玩家的生命能量值为:0 9 9
第2轮PK,最小的2个玩家是玩家2和玩家3,生命能力值分别是9和9,同归于尽,最后游戏没有剩余玩家,返回−1