2021 RoboCom 世界机器人开发者大赛-本科组(复赛)

举报
小哈里 发表于 2022/05/05 23:29:53 2022/05/05
【摘要】 文章目录 7-1 冒险者分队 207-2 拼题A打卡奖励 257-3 快递装箱 257-4 塔防游戏 30 7-1 冒险者分队 20 7-1 冒险者分队 分数 20 作者 DAI...

7-1 冒险者分队 20

7-1 冒险者分队
分数 20
作者 DAI, Longao
单位 杭州百腾教育科技有限公司
冒险者分队是人气 MMORPG《最终幻想 14》里的一个游戏系统。玩家通过招募 NPC (非玩家角色)组成小队完成特定任务后可以获取丰厚的奖励。

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

提升其中一个属性 40,降低其他两个属性各 20;
提升其中两个属性 20,降低剩下一个属性 40。
如果在选择的训练课程后有任意一个属性小于 0,那么训练会失败,属性不会发生变化。

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

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

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

所有属性值均大于等于 0,小于等于 2×10
9

输出格式:
如果目标属性无法通过训练达到,输出一行 −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

  • 题意:给出初始数组和目标数组,长都为3。每次可以给两个数+20,一个-40,或者两个-20,一个加40,求最少多少次能变成目标数组。
  • 思路:
    • 首先,初始数组和目标数组之间只能通过20来变换,所以对应位置的值作差%20,有余数肯定直接-1。然后把三个差值/20,简化问题。
    • 然后,考虑非法的情况,不管是-1,-1,+ 2还是 -2,+1,+1,这三个数%3的值都是1,因为最后要让差值从(a,b,c)变成(0,0,0),所以原本的这三个差值必须%3相等,否则-1。
    • 最后,考虑构造最优解。 设a < b < 0 < c,我们以1,1,-2作用于三者,那么b会首先变成0。再然后,再以两个操作2,-1,-1和1,1,-2组合成3,0,-3作用于a和c,就可以构造出(0,0,0)。(如果a或c不能被3整除怎么办?事实是不会的,因为上面已经判过三者对3同余了,所以三者%3都等于0,必定是能整除的)
#include<bits/stdc++.h>
using namespace std;

int a[10], b[10], c[10];

int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int T;  cin>>T;
    while(T--){
        for(int i = 1; i <= 3; i++)cin>>a[i];
        //-1
        int ok = 1;
        for(int i = 1; i <= 3; i++){
            cin>>b[i];  c[i] = b[i]-a[i];
            if(c[i]%20!=0)ok = 0;
            c[i] /= 20;
        }
        if(a[1]+a[2]+a[3] != b[1]+b[2]+b[3])ok = 0;
        int mod = (c[1]%3+3)%3;
        for(int i = 2; i <= 3; i++)
            if((c[i]%3+3)%3 != mod)ok = 0;
        if(ok==0){ cout<<"-1\n";  continue; }
        //构造
        int num = 0;
        num += (c[1]>0)+(c[2]>0)+(c[3]>0);
        if(num==2)c[1]=-c[1], c[2]=-c[2], c[3]=-c[3];
        int x = min({c[1],c[2],c[3]}), z = max({c[1],c[2],c[3]}), y = c[1]+c[2]+c[3]-x-z;//排序
        int res = 0;
        res += -y;  x -= y;  z += y*2;  y = 0; //操作1
        res += z/3*2;  //操作2
        cout<<res<<"\n";
    }
    return 0;
}



  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36

7-2 拼题A打卡奖励 25

7-2 拼题A打卡奖励
分数 25
作者 陈越
单位 浙江大学
拼题 A 的教超搞打卡活动,指定了 N 张打卡卷,第 i 张打卡卷需要 m
i

分钟做完,完成后可获得 c
i

枚奖励的金币。活动规定每张打卡卷最多只能做一次,并且不允许提前交卷。活动总时长为 M 分钟。请你算出最多可以赢得多少枚金币?

输入格式:
输入首先在第一行中给出两个正整数 N(≤10
3
) 和 M(≤365×24×60),分别对应打卡卷的数量和以“分钟”为单位的活动总时长(不超过一年)。随后一行给出 N 张打卡卷要花费的时间 m
i

(≤600),最后一行给出 N 张打卡卷对应的奖励金币数量 c
i

(≤30)。上述均为正整数,一行内的数字以空格分隔。

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

输入样例:
5 110
70 10 20 50 60
28 1 6 18 22
输出样例:
40
样例解释:
选择最后两张卷子,可以在 50+60=110 分钟内获得 18+22=40 枚金币。

  • 题意:n个物品,有价值和体积,每个可以选一次,背包容量m,求最大价值。
  • 思路:
    • 01背包板子题???果然TLE(15分)。
      n < 1e3, m < 5e5,所以O(nm)1e8肯定会超时。
    • 发现金币数量ci < 30 成为一个突破口,此时所有物品总价值不超过1000∗30,因此可以转换原本的求 “最小容量(5e5)下的最大价值” 为 “最大价值(3e4)下的最小容量”,系数都是n,3e7勉强可以通过。
//15分
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int v[maxn], w[maxn];
int f[maxn*maxn];
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int n, m;  cin>>n>>m;
    for(int i = 1; i <= n; i++)cin>>v[i];
    for(int i = 1; i <= n; i++)cin>>w[i];
    for(int i = 1; i <= n; i++){
        for(int j = m; j >= 0; j--){
            if(j>=v[i])f[j] = max(f[j], f[j-v[i]]+w[i]);
        }
    }
    cout<<f[m]<<"\n";
    return 0;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
//25分
#include<bits/stdc++.h>
using namespace std;
const int maxn = 1010;
int v[maxn], w[maxn];
int f[maxn*maxn];
int main(){
    ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
    int n, m;  cin>>n>>m;
    int sum = 0;
    for(int i = 1; i <= n; i++)cin>>v[i];
    for(int i = 1; i <= n; i++)cin>>w[i], sum += w[i];
    memset(f,0x3f,sizeof(f));
    f[0] = 0;
    for(int i = 1; i <= n; i++){
        for(int j = sum; j >= w[i]; j--){
            f[j] = min(f[j], f[j-w[i]]+v[i]);
        }
    }
    for(int i = sum; i >= 0; i--){
        if(f[i]<=m){
            cout<<i;  return 0;
        }
    }
    return 0;
}



  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

7-3 快递装箱 25

7-3 快递装箱
分数 25
作者 陈越
单位 浙江大学
bt.png

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

fig.png

快递件从 A 口进入流水线转盘;到达 B 时进行称重,如果重量大于 W
1

就开一个新的箱子把它装进去,否则直接让它通过;到达C时检查箱子的重量,如果超过了 W
2

(>W
1

),就直接装车 —— 这条线有 1 号摄像头拍摄装车的箱子编号并记录;重量不达标的箱子或快递件继续行进到 D,在这里进行装箱处理。这里分几种情况:

如果 D 当前是空的,那么新开一箱给到达的快递件,或者如果到达的是一只箱子,那么等待下一个快递件到达;
如果 D 当前不是空的,那么肯定是有一只箱子。这时考察下一个物体 —— 如果下一个是快递件并且能装入这个箱子(即总重量不超过箱子的最大容量 W
max

),则将其装入;如果下一个是快递件但装不下了(这时新的快递件装箱),或者下一个来的就是箱子,或者已经没有货物过来了,则停止当前的装箱工作,检查当前这个箱子的重量,超过 W
2

就装车 —— 这条线有 2 号摄像头拍摄装车的箱子编号并记录;如果重量不足,则继续前进到 A 口,与新到的快递件汇合。D 点继续处理下一个排队的箱子或快递件。
而到 A 口的箱子则要看汇合的快递件能否装入:如果可以就装箱,向 B 进发;不行就一直等待,直到下一个可以装箱的快递装进去,或者没有任何新的快递到达,才继续向 B 进发。当有多只箱子从 D 转过来时,按到达的顺序排队。

简单起见,我们假设快递件匀速进入流水线,所有快递件从一个点到下一个点都只需要一个单位时间,并且在 B 和 C 的停留时间可忽略不计。当 B 发现重量大于 W
1

的是已经在箱子里的货物时,则不必再新开一个箱子。

输入格式:
输入在第一行给出 4 个正整数:N 为快递件的数量;W
max

、W
1

和 W
2

,如题面所述。其中 N≤10
4
,W
1

<W
2

<W
max

≤10
3

随后一行给出 N 个不超过 W
max

的正整数,为顺序到达的快递件的重量。

输出格式:
在第一行中先后输出第 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
样例说明:
我们按照“单位时间:事件”的格式描述整个过程。

时间 A B C D
1 85 - - -
2 25 85装箱 - -
3 60 25 85装车,1号拍摄 -
4 21 60装箱 25 -
5 10 21 60一箱 25装箱
6 52 10 21 60一箱到,25一箱启动
7 80不能装箱,25一箱等待 52装箱 10 21装箱得到81一箱
8 95不能装箱,25一箱等待 80装箱 52一箱 10装箱得到91一箱
9 78不能装箱,25一箱等待 95装箱 80一箱 52一箱到,91一箱装车,2号拍摄
10 15装箱得到40一箱 78装箱 95一箱装车,1号拍摄 80一箱到,52一箱启动
11 3装箱得到55一箱 40一箱 78一箱 80一箱
此时摄像头 1 拍摄了 2 次,摄像头 2 拍摄了 1 次,转盘上还剩 4 只重量为 55、40、78 和 80 的箱子。

  • 题意:模拟流水线,塞个货物进传送带,然后往后运动,更新一下每个点的状态,如果有货物运出就答案加一,照着题目意思做就行。
  • 思路:
    A、D 点用双端队列处理(因为有塞回去的情况),B、C用普通队列处理。
    用一个 pair 存储货物状态,分别记录重量和是否装箱,每次按题目要求更新即可。
#include<bits/stdc++.h>
using namespace std;
typedef pair<int, int> PII;
const int maxn = 1010;

int n, wmax, w1, w2;
vector<int>ans;
int ans1, ans2;

deque<PII>A, D;
queue<PII>B, C;
void update(){  //每次按DCBA的顺序处理一遍当前流水线上的箱子
    if(D.size()!=0){
        PII x = D.front();
        if(x.second == 0){ //x没装箱,给x装箱
            D.pop_front();
            D.push_front(make_pair(x.first,1));
        }else{
            D.pop_front();
            if(D.size()==0)D.push_front(x);//x不是最后一个就纳入出队考核
            else{
                PII y = D.front();
                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{
                        if(x.first>w2)ans2++; //x正式出队
                        else A.push_back(x);  //给x放回去A重生
                    }
                }else{
                    if(x.first > w2)ans2++;  //x正式出队
                    else A.push_back(x);     //给x放回去A重生
                }
                if(D.front().second == 0){ //z没装箱,给z装箱
                    PII z = D.front();  D.pop_front();
                    D.push_front(make_pair(z.first,1));
                }
            }
        }
    }
    if(C.size()!=0){
        PII x = C.front();  C.pop();
        if(x.first > w2)ans1++;
        else D.push_back(x);
    }
    if(B.size()!=0){
        PII x = B.front();  B.pop();
        if(x.first > w1)C.push(make_pair(x.first, 1));
        else C.push(x);
    }
    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;  //把x放到A口,考虑是否装箱
        if(A.size()==0)A.push_back(make_pair(x,0));
        else{
            PII y = A.front();
            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(); //更新一下
    }
    //solve
    for(int i = 1; i <= 10050; i++)update();//让流水线转很多轮
    queue<PII>D2; //临时队列
    while(D.size() != 0){
        PII x = D.front();  D.pop_front();
        if(x.first > w2)ans2++; else D2.push(x);
    }
    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
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96

7-4 塔防游戏 30

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 秒就结束了。

  • 题意:T秒为一轮游戏的时长,然后n行m列的地图为玩家领地,领地中有防御为-D的大本营和防御为a[i][j]的堡垒,防御值可以和僵尸1:1换血。僵尸从四周一圈(n+2行,m+2列)往中间走,走的路线为开局时被歼灭最少的去大本营的路线。求最后僵尸能不能打下大本营。
  • 思路:
    • 首先:比较难搞的就是开局时僵尸走的最短路线,因为起点不同而终点一致,我们可以从终点出发的跑最短路,因为是网格图,所以可以跑带优先队列的 Dijkstra不容易被卡。
    • 然后:确定了僵尸的行走路线之后,就是按照时间移动僵尸模拟即可。做法就是维护僵尸在的位置,每次扫一遍所有的僵尸,对于还活着的僵尸,尝试向最短路路径上的下一个点移动一步,如果有堡垒挡住就原地打堡垒。
    • 坑点:当3个僵尸同时攻打某个防御值为2的堡垒,该堡垒消失的同时,3个僵尸都会被扣1滴血
#include<bits/stdc++.h>
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];
}

int dist[maxn][maxn], vis[maxn][maxn], tim[maxn][maxn];//Dij
PII path[maxn][maxn];

int main(){
    ios::sync_with_stdio(0), cin.tie(0),cout.tie(0);
    //input&init
    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};//init
            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.size()){
        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]+a[nx][ny]){//找消耗最小的
                dist[nx][ny] = dist[pos.first][pos.second]+a[nx][ny];
                path[nx][ny] = pos;
                tim[nx][ny] = tim[pos.first][pos.second]+1;
            }else if(dist[nx][ny] == dist[pos.first][pos.second]+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.first = nx;  tmp.second = ny;  break;
            }
        }
        while(1){  //用Dij的path为出发的僵尸it{x,y}选择最优路径
            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)){
                se.insert({x,y});
            }else if(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";
    }
    if(a[st.first][st.second]==0)cout<<"Game Over\n";
    return 0;
}



  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121

文章来源: gwj1314.blog.csdn.net,作者:小哈里,版权归原作者所有,如需转载,请联系作者。

原文链接:gwj1314.blog.csdn.net/article/details/124567022

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

举报
请填写举报理由
0/200