7-1 冒险者分队

分数 20

作者 DAI, Longao

单位 杭州百腾教育科技有限公司

冒险者分队是人气 MMORPG《最终幻想 14》里的一个游戏系统。玩家通过招募 NPC (非玩家角色)组成小队完成特定任务后可以获取丰厚的奖励。

由于完成任务有能力的要求,因此我们需要对 NPC 进行一定的训练。NPC 组成的小队会有三个属性:体能、心智,以及战术,玩家可以选择以下的两种训练课程之一对小队进行训练:

  • 提升其中一个属性 40,降低其他两个属性各 20;
  • 提升其中两个属性 20,降低剩下一个属性 40。

如果在选择的训练课程后有任意一个属性小于 0,那么训练会失败,属性不会发生变化。

为了完成特定任务,现在给定小队的初始属性和目标属性,请回答是否有可能通过一定的训练,使得小队的属性正好达到目标属性的值,如果可以的话,最少的次数是多少?

输入格式:

输入第一行是一个正整数 T (≤105),表示有多少组询问。

接下来的 T 组询问,每组询问有两行,每行三个非负整数,第一行为小队初始的属性,第二行为需要达成的目标属性。

所有属性值均大于等于 0,小于等于 2×109。

输出格式:

如果目标属性无法通过训练达到,输出一行 −1,否则输出一个整数,表示达到目标属性的最少训练次数。

输入样例:

4
25 30 35
65 10 15
100 200 300
200 180 220
100 100 100
0 0 0
777 888 999
777 888 999

输出样例:

1
3
-1
0
#include <iostream>
#include <algorithm>
using namespace std;

void solve(){
    long long a, b, c, x, y, z;
    cin >> a >> b >> c >> x >> y >> z;
    
    // 计算差值(注意:这里是 当前-目标,与我之前的相反)
    long long dx = a - x, dy = b - y, dz = c - z;
    
    // 基本可行性检查
    if (dx % 20 || dy % 20 || dz % 20 || a + b + c != x + y + z){
        cout << -1 << endl;
        return;
    }
    
    // 转换为基本单位
    dx /= 20; dy /= 20; dz /= 20;
    
    // 关键约束:模3同余性检查
    int mod = (dx % 3 + 3) % 3;
    if ((dy % 3 + 3) % 3 != mod || (dz % 3 + 3) % 3 != mod){
        cout << -1 << endl;
        return;
    }
    
    // 统一处理方向(确保大多数是负数)
    int positive_count = (dx > 0) + (dy > 0) + (dz > 0);
    if (positive_count == 2) {
        dx = -dx; dy = -dy; dz = -dz;
    }
    
    // 排序:x ≤ y ≤ z
    long long vals[3] = {dx, dy, dz};
    sort(vals, vals + 3);
    long long x_min = vals[0], y_mid = vals[1], z_max = vals[2];
    
    long long result = 0;
    
    // 先处理中间值
    result += -y_mid;
    x_min -= y_mid;
    z_max += y_mid * 2;
    y_mid = 0;
    
    // 处理剩余部分
    result += z_max / 3 * 2;
    
    cout << result << endl;
}

int main(){
    ios::sync_with_stdio(false);
    cin.tie(0);
    int t;
    cin >> t;
    while (t--) solve();
    return 0;
}

7-2 拼题A打卡奖励

分数 25

作者 陈越

单位 浙江大学

拼题 A 的教超搞打卡活动,指定了 N 张打卡卷,第 i 张打卡卷需要 mi​ 分钟做完,完成后可获得 ci​ 枚奖励的金币。活动规定每张打卡卷最多只能做一次,并且不允许提前交卷。活动总时长为 M 分钟。请你算出最多可以赢得多少枚金币?

输入格式:

输入首先在第一行中给出两个正整数 N(≤103) 和 M(≤365×24×60),分别对应打卡卷的数量和以“分钟”为单位的活动总时长(不超过一年)。随后一行给出 N 张打卡卷要花费的时间 mi​(≤600),最后一行给出 N 张打卡卷对应的奖励金币数量 ci​(≤30)。上述均为正整数,一行内的数字以空格分隔。

输出格式:

在一行中输出最多可以赢得的金币数量。

输入样例:

5 110
70 10 20 50 60
28 1 6 18 22

输出样例:

40

样例解释:

选择最后两张卷子,可以在 50+60=110 分钟内获得 18+22=40 枚金币。

 

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

const int INF = 1e9;

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int N, M;
    cin >> N >> M;
    
    vector<int> time(N), coins(N);
    
    for (int i = 0; i < N; i++) {
        cin >> time[i];
    }
    
    for (int i = 0; i < N; i++) {
        cin >> coins[i];
    }
    
    // 计算最大可能的总价值
    int max_value = 0;
    for (int i = 0; i < N; i++) {
        max_value += coins[i];
    }
    
    // dp[v] 表示获得价值v所需的最少时间
    vector<int> dp(max_value + 1, INF);
    dp[0] = 0;
    
    for (int i = 0; i < N; i++) {
        // 从后往前更新,避免重复使用
        for (int v = max_value; v >= coins[i]; v--) {
            if (dp[v - coins[i]] != INF) {
                dp[v] = min(dp[v], dp[v - coins[i]] + time[i]);
            }
        }
    }
    
    // 找到在M时间内能获得的最大价值
    int result = 0;
    for (int v = 0; v <= max_value; v++) {
        if (dp[v] <= M) {
            result = v;
        }
    }
    
    cout << result << endl;
    
    return 0;
}

 

7-3 快递装箱

分数 25

作者 陈越

单位 浙江大学

bt.png

有一条快递装箱的流水线是这样设计的(见下图):

fig.png

快递件从 A 口进入流水线转盘;到达 B 时进行称重,如果重量大于 W1​ 就开一个新的箱子把它装进去,否则直接让它通过;到达C时检查箱子的重量,如果超过了 W2​(>W1​),就直接装车 —— 这条线有 1 号摄像头拍摄装车的箱子编号并记录;重量不达标的箱子或快递件继续行进到 D,在这里进行装箱处理。这里分几种情况:

  • 如果 D 当前是空的,那么新开一箱给到达的快递件,或者如果到达的是一只箱子,那么等待下一个快递件到达;
  • 如果 D 当前不是空的,那么肯定是有一只箱子。这时考察下一个物体 —— 如果下一个是快递件并且能装入这个箱子(即总重量不超过箱子的最大容量 Wmax​ ),则将其装入;如果下一个是快递件但装不下了(这时新的快递件装箱),或者下一个来的就是箱子,或者已经没有货物过来了,则停止当前的装箱工作,检查当前这个箱子的重量,超过 W2​ 就装车 —— 这条线有 2 号摄像头拍摄装车的箱子编号并记录;如果重量不足,则继续前进到 A 口,与新到的快递件汇合。D 点继续处理下一个排队的箱子或快递件。

而到 A 口的箱子则要看汇合的快递件能否装入:如果可以就装箱,向 B 进发;不行就一直等待,直到下一个可以装箱的快递装进去,或者没有任何新的快递到达,才继续向 B 进发。当有多只箱子从 D 转过来时,按到达的顺序排队。

简单起见,我们假设快递件匀速进入流水线,所有快递件从一个点到下一个点都只需要一个单位时间,并且在 B 和 C 的停留时间可忽略不计。当 B 发现重量大于 W1​ 的是已经在箱子里的货物时,则不必再新开一个箱子。

输入格式:

输入在第一行给出 4 个正整数:N 为快递件的数量;Wmax​、W1​ 和 W2​,如题面所述。其中 N≤104,W1​<W2​<Wmax​≤103。

随后一行给出 N 个不超过 Wmax​ 的正整数,为顺序到达的快递件的重量。

输出格式:

在第一行中先后输出第 1、2 号摄像头拍摄的装车箱子的数量、以及最后转盘上剩下的箱子的数量。第二行按非递减序输出剩下的箱子的重量。如果没有箱子剩下,则输出 None

同一行数字间以 1 个空格分隔,行首尾不得有多余空格。

输入样例:

11 100 50 80
85 25 60 21 10 52 80 95 78 15 3

输出样例:

2 1 4
40 55 78 80

样例说明:

我们按照“单位时间:事件”的格式描述整个过程。

时间ABCD
185---
22585装箱--
3602585装车,1号拍摄-
42160装箱25-
5102160一箱25装箱
652102160一箱到,25一箱启动
780不能装箱,25一箱等待52装箱1021装箱得到81一箱
895不能装箱,25一箱等待80装箱52一箱10装箱得到91一箱
978不能装箱,25一箱等待95装箱80一箱52一箱到,91一箱装车,2号拍摄
1015装箱得到40一箱78装箱95一箱装车,1号拍摄80一箱到,52一箱启动
113装箱得到55一箱40一箱78一箱80一箱

此时摄像头 1 拍摄了 2 次,摄像头 2 拍摄了 1 次,转盘上还剩 4 只重量为 55、40、78 和 80 的箱子。

 

#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;  // (重量, 装箱状态): 0-未装箱, 1-已装箱
const int maxn = 1010;

int n, wmax, w1, w2;  // 快递件数量, 箱子最大容量, 装箱阈值, 装车阈值
vector<int> ans;      // 存储最终剩余箱子的重量
int ans1, ans2;       // 1号摄像头和2号摄像头拍摄次数

// 流水线各个位置的队列
deque<PII> A, D;  // A口和D点使用双端队列,方便前后操作
queue<PII> B, C;  // B点和C点使用普通队列

/**
 * 核心函数:更新流水线状态
 * 按照 D→C→B→A 的逆序处理,模拟一个时间单位内的流水线传递
 */
void update() {
    // ==================== D点处理逻辑 ====================
    // D点是装箱处理中心,逻辑最复杂
    if (D.size() != 0) {
        PII x = D.front();  // 取出当前正在处理的物品
        
        if (x.second == 0) {  // 如果x是未装箱的快递件
            D.pop_front();
            D.push_front(make_pair(x.first, 1));  // 给x开新箱装箱
        } else {  // 如果x是已装箱的箱子
            D.pop_front();
            
            if (D.size() == 0) {  // 如果x是队列中最后一个物品
                D.push_front(x);  // 放回去继续等待
            } else {
                PII y = D.front();  // 查看下一个物品y
                
                if (y.second == 0) {  // 如果y是未装箱的快递件
                    if (y.first + x.first <= wmax) {  // 如果y能装入x箱子
                        D.pop_front();
                        D.push_front(make_pair(x.first + y.first, 1));  // 合并装箱
                    } else {  // y装不下,x箱子完成装箱
                        if (x.first > w2) ans2++;      // 重量超过w2,2号摄像头拍摄装车
                        else A.push_back(x);           // 重量不足,返回A口重新处理
                    }
                } else {  // 如果y也是箱子,x箱子完成装箱
                    if (x.first > w2) ans2++;          // 重量超过w2,2号摄像头拍摄装车
                    else A.push_back(x);               // 重量不足,返回A口重新处理
                }
                
                // 处理队列前端的快递件(如果有的话)
                if (D.size() > 0 && D.front().second == 0) {
                    PII z = D.front(); 
                    D.pop_front();
                    D.push_front(make_pair(z.first, 1));  // 给z开新箱装箱
                }
            }
        }
    }
    
    // ==================== C点处理逻辑 ====================
    // C点负责检查箱子重量,决定装车还是继续到D点
    if (C.size() != 0) {
        PII x = C.front(); 
        C.pop();
        if (x.first > w2) ans1++;     // 重量超过w2,1号摄像头拍摄装车
        else D.push_back(x);          // 重量不足,送到D点处理
    }
    
    // ==================== B点处理逻辑 ====================
    // B点负责对重快递件装箱
    if (B.size() != 0) {
        PII x = B.front(); 
        B.pop();
        if (x.first > w1) C.push(make_pair(x.first, 1));  // 重量>w1装箱后送到C
        else C.push(x);                                    // 重量<=w1直接送到C
    }
    
    // ==================== A点处理逻辑 ====================
    // A点将物品送入流水线
    if (A.size() != 0) {
        B.push(A.front());  
        A.pop_front();
    }
}

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    cin >> n >> wmax >> w1 >> w2;
    
    // 处理每个到达的快递件
    for (int i = 1; i <= n; i++) {
        int x; 
        cin >> x;
        
        // ==================== A口新快递件处理 ====================
        if (A.size() == 0) {
            A.push_back(make_pair(x, 0));  // A口为空,直接放入
        } else {
            PII y = A.front();  // 查看A口前端的物品
            if (y.first + x <= wmax) {
                // 如果新快递件能装入前端物品的箱子
                A.pop_front();
                A.push_front(make_pair(y.first + x, 1));  // 合并装箱,放到队首优先处理
            } else {
                // 装不下,新快递件排到队首等待处理
                A.push_front(make_pair(x, 0));
            }
        }
        
        update();  // 每来一个快递件,更新一次流水线状态
    }
    
    // ==================== 流水线空转处理 ====================
    // 让流水线继续运转,直到所有物品都处理完毕
    for (int i = 1; i <= 10050; i++) {
        update();
    }
    
    // ==================== 最终D点清理 ====================
    // 处理D点剩余的箱子
    queue<PII> D2;  // 临时队列
    while (D.size() != 0) {
        PII x = D.front(); 
        D.pop_front();
        if (x.first > w2) ans2++;   // 超重装车
        else D2.push(x);            // 不超重的放入临时队列
    }
    // 将不超重的箱子放回D队列
    while (D2.size() != 0) { 
        D.push_back(D2.front());  
        D2.pop(); 
    }
    
    // ==================== 收集剩余箱子 ====================
    // 统计各个位置剩余的箱子重量
    while (A.size() != 0) {
        if (A.front().first != 0) ans.push_back(A.front().first); 
        A.pop_front(); 
    }
    while (B.size() != 0) {
        if (B.front().first != 0) ans.push_back(B.front().first); 
        B.pop(); 
    }
    while (C.size() != 0) {
        if (C.front().first != 0) ans.push_back(C.front().first); 
        C.pop(); 
    }
    while (D.size() != 0) {
        if (D.front().first != 0) ans.push_back(D.front().first); 
        D.pop_front(); 
    }
    
    // ==================== 输出结果 ====================
    sort(ans.begin(), ans.end());  // 按非递减序排序
    
    cout << ans1 << " " << ans2 << " " << ans.size() << "\n";
    
    if (ans.size() == 0) {
        cout << "None\n";
    } else {
        for (int i = 0; i < ans.size(); i++) {
            cout << ans[i] << " \n"[i == ans.size() - 1];  // 最后一个元素后输出换行
        }
    }
    
    return 0;
}

/*
算法核心思想:
1. 使用pair<int,int>表示(重量, 装箱状态),0表示未装箱,1表示已装箱
2. A和D使用deque,支持双端操作;B和C使用queue,符合FIFO特性
3. update函数按D→C→B→A逆序处理,模拟流水线传递
4. 每来一个快递件就调用一次update,最后空转确保所有物品处理完
5. 通过巧妙的队列操作避免了复杂的时间模拟

时间复杂度:O(N + 常数),空间复杂度:O(N)
*/

 

7-4 塔防游戏

分数 30

作者 陈越

单位 浙江大学

tf.JPG

有一种简单的塔防游戏是这样的:给定一张由 n 行 m 列个方格子构成的地图,玩家可以任选一个格子放置自己的大本营,还可以在任意一个格子里放置自己的防御堡垒。大本营和每个防御堡垒都有自己的防御能力值 d,表示可以抵御 d 个僵尸的攻击。每一轮游戏开始时,玩家在规定时间内将本级别可以用的防御堡垒布置在地图中,然后僵尸们就从地图边界涌入地图中,向着大本营发起攻击。每轮进攻持续一个固定的时长,结束后剩余的僵尸就原地蒸发。

每队僵尸可以向一个方格的上下左右四个方向移动。如果相邻的目标方格没有堡垒,它们就可以用 1 秒的时间移动过去,否则会被堡垒阻挡或者消灭。对每一队僵尸(从同一地点出发的所有僵尸)而言,每秒会被堡垒消灭 1 个队友,同时消耗掉该堡垒 1 个单位的防御能力。当防御能力降为 0,则该堡垒消失,剩下的僵尸则用 1 秒移动到这个方格继续行进。注意:如果有多支僵尸队都进入了同一个方格,它们并不会合并成一支队伍。

所有的僵尸队都会根据进攻开始时的地图选择被歼灭最少的到达大本营的路线,并且一直按照这个路线行进,中途不因为地图状态的改变而改变。当这样的进攻路径不唯一时,选择能最快到达大本营的路径。题目保证这样的路径所打掉的堡垒的布局是唯一的。

本题就要求你计算出一轮攻击结束时,地图上的布局情况。

输入格式:

输入首先在第一行中给出三个正整数:不超过 100 的 n 和 m,为地图的尺寸;不超过 1000 的 T,为一轮攻击持续的时长。

随后给出 n+2 行,每行给出 m+2 个数字,每行中的数字都用空格分隔,表示攻击开始前地图上的布局。其中第 1 行、第 1 列、第 n+2 行、第 m+2 列是地图边界外僵尸们出发的位置,这些位置上,0 表示没有僵尸,其他正整数表示从该位置出发的僵尸们的数量。而地图中的每个位置上,0 表示没有堡垒,其它正整数表示该位置上堡垒的防御能力值。大本营是一个特殊的建筑,我们用一个负数 −D 表示这里是大本营,其防御能力值为 D。这里的防御值和任一队僵尸的数量都不超过 100。

注意:僵尸不可在地图边界外移动,它们的第一个移动目标必须在地图中,所以四个角落里出现的僵尸可以被忽略,因为它们没有进入地图的途径。

输出格式:

输出 n 行,每行 m 个数字,对应攻击结束后地图上每个方格的状态。状态的表示与输入相同:没有堡垒的地方输出 0,有堡垒的地方输出其剩余防御值,大本营的位置上输出其剩余防御值的负值。

注意每行数字间以 1 个空格分隔,行首尾不得有多余空格。

当大本营被攻陷时,游戏即刻结束。此时应输出结束时的地图状态,并且在最后一行输出一句 Game Over

输入样例 1:

7 5 17
0 0 0 0 13 0 0
0 0 0 0 0 0 0
0 0 0 8 0 0 0
0 0 0 0 2 1 0
0 0 0 7 5 3 0
8 0 1 4 -10 1 0
0 0 0 3 3 0 0
0 0 8 0 9 0 0
0 0 0 4 0 0 0

输出样例 1:

0 0 0 0 0
0 0 8 0 0
0 0 0 2 0
0 0 7 5 0
0 0 0 -1 0
0 0 0 2 0
0 8 0 9 0

样例说明:

地图布局如下图所示。

map.JPG

规模为 13 和 8 的两队僵尸都有两种选择,攻打蓝色或者紫色堡垒都是消耗最少的。在这种情况下,规模为 13 的僵尸队走蓝色比较快,需要 1+1+1+2+4+2=11 秒到达大本营边上;规模为 8 的僵尸队走紫色比较快,需要 1+2+5=8 秒到达大本营边上。

规模为 4 的僵尸队比较惨,只能选择绿色堡垒,最后被大本营边上的绿色堡垒消灭。注意到在攻击过程中,其实它们可以等到紫色堡垒被攻陷之后走紫色原始值为 4 的方格,但是因为路径是在初始状态下选定就不能改的,所以它们不能这样选择。

攻打大本营时,规模为 8 的僵尸队剩下了 3 只先到达,在第 11 秒被大本营消灭。此时大本营还剩 7 个单位的防御值,同时规模为 13 的僵尸队剩下的 8 只进入了大本营相邻的方格,开始攻击。但此时距离本轮结束只剩 6 秒,结果大本营在结束时还剩 1 个单位的防御值,玩家胜。

输入样例 2:

7 5 20
0 0 0 0 13 0 0
0 0 0 0 0 0 0
0 0 0 8 0 0 0
0 0 0 0 2 1 0
0 0 0 7 5 3 0
8 0 1 4 -10 1 0
0 0 0 3 3 0 0
0 0 8 0 9 0 0
0 0 0 4 0 0 0

输出样例 2:

0 0 0 0 0
0 0 8 0 0
0 0 0 2 0
0 0 7 5 0
0 0 0 0 0
0 0 0 2 0
0 8 0 9 0
Game Over

样例说明:

样例 2 与样例 1 唯一的区别在于攻击时长变为 20。但实际上,攻击在第 18 秒就结束了。

#include <iostream>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <algorithm>

using namespace std;

typedef pair<int, int> PII;
typedef pair<int, PII> PIII;
const int maxn = 220, inf = 1e9+10;

int n, m, T, a[maxn][maxn]; // 地图
int dx[] = {-1, 1, 0, 0}, dy[] = {0, 0, -1, 1}; // 四个方向
PII st; // 大本营坐标

struct node {
    int x, y, cc; // 坐标和血量
};
vector<node> js; // 僵尸坐标和血量

int cnt[maxn*maxn], now[maxn*maxn]; // 每个僵尸剩余血量, 该僵尸走了第几步
vector<PII> p[maxn*maxn]; // 每个僵尸队伍的路线

map<PII, int> mp; // 给僵尸队伍坐标离散化为数字
int idx = 1;

// 将坐标映射为唯一编号
int getidx(PII x) {
    if (!mp.count(x)) mp[x] = idx++;
    return mp[x];
}

// Dijkstra算法所需变量
int dist[maxn][maxn], vis[maxn][maxn], tim[maxn][maxn];
PII path[maxn][maxn]; // 记录路径

int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    
    // 输入与初始化
    cin >> n >> m >> T;
    for (int i = 0; i <= n+1; i++) {
        for (int j = 0; j <= m+1; j++) {
            // 初始化
            dist[i][j] = inf;
            path[i][j] = {-1, -1};
            vis[i][j] = 0;
            tim[i][j] = 0;
            
            cin >> a[i][j];
            
            // 记录大本营位置
            if (a[i][j] < 0) st = {i, j};
            
            // 跳过四个角落
            if ((i==0&&j==m+1) || (i==0&&j==0) || (i==n+1&&j==0) || (i==n+1&&j==m+1)) continue;
            
            // 记录边界上的僵尸
            if ((i<1 || i>n || j<1 || j>m) && a[i][j] > 0) {
                js.push_back({i, j, a[i][j]});
            }
        }
    }
    
    // 从大本营开始用Dijkstra算法计算最短路径
    priority_queue<PIII, vector<PIII>, greater<PIII>> q;
    q.push({0, {st.first, st.second}}); // {消耗, 坐标}
    dist[st.first][st.second] = 0;
    
    while (!q.empty()) {
        auto t = q.top();
        q.pop();
        PII pos = t.second;
        
        if (vis[pos.first][pos.second]) continue;
        vis[pos.first][pos.second] = 1;
        
        // 探索四个方向
        for (int i = 0; i < 4; i++) {
            int nx = pos.first + dx[i], ny = pos.second + dy[i];
            if (nx < 1 || nx > n || ny < 1 || ny > m) continue;
            
            // 更新最短路径:优先考虑消耗最小的
            if (dist[nx][ny] > dist[pos.first][pos.second] + abs(a[nx][ny])) {
                dist[nx][ny] = dist[pos.first][pos.second] + abs(a[nx][ny]);
                path[nx][ny] = pos;
                tim[nx][ny] = tim[pos.first][pos.second] + 1;
                q.push({dist[nx][ny], {nx, ny}});
            } 
            // 如果消耗相等,选择时间最短的
            else if (dist[nx][ny] == dist[pos.first][pos.second] + abs(a[nx][ny])) {
                if (tim[nx][ny] > tim[pos.first][pos.second] + 1) {
                    tim[nx][ny] = tim[pos.first][pos.second] + 1;
                    path[nx][ny] = pos;
                    q.push({dist[nx][ny], {nx, ny}});
                }
            }
        }
    }
    
    // 为每个僵尸队伍计算最佳路径
    for (auto it : js) {
        int id = getidx({it.x, it.y});
        cnt[id] = it.cc; // 记录僵尸数量
        
        // 找到僵尸进入地图的第一个位置
        PII tmp;
        for (int i = 0; i < 4; i++) {
            int nx = it.x + dx[i], ny = it.y + dy[i];
            if (nx >= 1 && nx <= n && ny >= 1 && ny <= m) {
                tmp = {nx, ny};
                break;
            }
        }
        
        // 根据Dijkstra的结果回溯路径
        while (1) {
            p[id].push_back(tmp);
            if (tmp.first == st.first && tmp.second == st.second) break;
            tmp = path[tmp.first][tmp.second];
        }
    }
    
    // 模拟攻击过程
    for (int i = 1; i <= T; i++) {
        // 记录当前被攻击的堡垒
        set<PII> se;
        
        // 确定僵尸位置
        for (auto it : js) {
            int id = getidx({it.x, it.y});
            if (cnt[id] == 0) continue;
            
            // 获取僵尸当前位置
            int x = p[id][now[id]].first, y = p[id][now[id]].second;
            
            // 记录被攻击的堡垒或大本营
            if ((a[x][y] > 0 && !(x == st.first && y == st.second)) || 
                (a[x][y] < 0 && (x == st.first && y == st.second))) {
                se.insert({x, y});
            }
        }
        
        // 处理僵尸攻击和移动
        for (auto it : js) {
            int id = getidx({it.x, it.y});
            if (cnt[id] == 0) continue;
            
            int x = p[id][now[id]].first, y = p[id][now[id]].second;
            
            // 如果当前位置没有被攻击,僵尸前进
            if (se.count({x, y}) == 0) {
                now[id] = min(now[id] + 1, (int)p[id].size() - 1);
            } 
            // 否则攻击并消耗僵尸
            else {
                if (a[x][y] > 0) a[x][y]--;      // 攻打堡垒
                else if (a[x][y] < 0) a[x][y]++; // 攻打大本营
                cnt[id]--;                       // 僵尸消耗
            }
        }
        
        // 检查大本营是否被攻陷
        if (a[st.first][st.second] == 0) break;
    }
    
    // 输出最终地图状态
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            if (j != 1) cout << " ";
            cout << a[i][j];
        }
        cout << "\n";
    }
    
    // 如果大本营被攻陷,输出Game Over
    if (a[st.first][st.second] == 0) cout << "Game Over\n";
    
    return 0;
}

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐