【NOIP模拟赛】战棋游戏

战棋游戏


  • Description

Rainbow擅长战棋类游戏。著名的战棋游戏有很多,例如《曹操传》、《瓦岗山异闻录》等。在本题中,我们考虑战棋游戏的一个简单版本,基于一下规则:

地图

地图是一个N行N列的矩阵,矩阵中的不同位置有不同的地形,例如平原、森林、山地、河流、雪原等。在这里,我们给矩阵中的每个位置 (i,j) 规定一个正整数 Gi,j ,这个整数会对在矩阵中的移动产生影响。

角色

每个角色均可被视作位于矩阵中某个位置上的任务。每个角色均有生命值 HP ,攻击力 AT ,移动力 MV 和攻击范围 [AD1,AD2] 。角色被分为两个阵营,第0阵营被玩家控制,第1阵营被系统控制。
当两个不同阵营的角色p和q的曼哈顿距离处于p的攻击范围内(包含端点)时,p可以攻击q。若q被p攻击,则q的生命值被减去p的攻击力。
当一个角色的生命值 0 时,他将立即被移除地图,离开游戏。

每个角色均占领地图上的一个位置,并且可以在地图上移动,遵循以下规则:

占领

最初,每个角色在地图的不同位置生成。他们各自占领自己所在的位置。
当一个角色在移动式,其他角色均不能移动。
当一个角色开始移动前失去对所在位置的占领权,停止移动时获得所在位置的占领权。

“移动”与“步”

一次移动由若干步组成。在每一步中,一个角色可以从当前位置走向四个方向上相邻的位置,但不能走到被其他角色占领的位置上。
当一个角色从 (i,j) 走到 (x,y) 时,它的移动力会被扣除 Gx,y 。使得移动力变为负数的“步”是不合法的。在合法的一“步”完成后,若该角色正处于与对方阵营某个角色相邻(四个方向)的位置上,则该角色的移动力当一次“移动”结束后,角色的移动力恢复为初始值。

能被一个角色通过若干合法“步”到达的位置集合称为该角色的移动范围。停留在原地也是合法的,故也被算入移动范围。
游戏的过程由若干轮组成,每轮玩家阵营先行动,系统阵营后行动。对于每个阵营,该阵营的所有角色轮流行动。在每次行动中,一个角色先移动到移动范围内的任意一个位置,然后可能会攻击对方阵营的一个角色(或什么也不做)。当一个阵营的所有角色均被移除出地图时,另外一个阵营获胜。

Rainbow得到了一份游戏日志,起帮他复原这个游戏的过程。

  • Input Format

第一行4个整数 N,M,C,E (1N,M100,2Cmin(NM,100),1E3000) ,表示地图行数、列数、角色数、日志事件数。
接下来 N 每行M 个整数 Gi,j (1Gi,j102)
接下来 C 行每行8个整数HP,AT,MV,AD1,AD2,STx,STy,GR,(1HP,AT,MV103,1AD1AD220,1STxN,1STyM,0GR1 ,表示角色的5个属性以及初始位置、所属阵营。
接下来 E 行描述日志中的事件,每个事件都是以下格式之一:
Round of GR 阵营 GR 行动回合开始。
Action of character ID 角色 ID 行动开始。
Move to (x,y) 当前行动角色通过若干步移动到 (x,y)
Attack ID 当前行动角色攻击角色 ID 但是角色 ID 在攻击后未被移除。
Drive out ID 当前行动角色攻击角色 ID 并造成 ID 被移除游戏。
在事件中, 1xN,1yM,1IDC,0GR1. 行动标号从1开始,每个阵营初始至少拥有1个角色。

  • Output Format

对于每个 Move to 事件,若无法到达,输出“INVALID” ,否则输出当它进入终点,准备结束“移动”前最多剩余多少移动力(移动力变化的优先顺序为:减去G→若与敌方阵营相邻扣为0→输出→恢复初始值)。
对于每个 Attack or Drive out 事件,若不能攻击或者攻击造成的是否移除地图的情况与输入不相符,输出“INVALID” ,否则输出攻击后被攻击角色剩余 HP
不合法的事件在处理后续事件时应当被忽略。数据保证输入完成时游戏恰好结束,并且 Round of and Action of 类事件被正确记录,没有确实或者矛盾。

  • Sample Input

5 5 4 12
1 9 1 4 4
1 9 1 2 4
1 9 1 1 2
2 9 2 7 3
2 3 2 6 1
10 5 8 1 3 1 1 0
20 10 5 1 2 2 1 0
19 10 5 1 2 5 2 1
25 25 3 1 1 5 5 0
Round of 0
Action of character 2
Move to (5,1)
Attack 3
Action of character 1
Move to (3,1)
Round of 1
Action of character 3
Drive out 1
Round of 0
Action of character 2
Drive out 3

  • Sample Output

0
9
6
INVALID
-1

  • Hint

【数据规模与约定】
对于80%的数据,不存在不合法事件。
对于100%的数据,参见输入输出格式中给定的范围与保证。


  • 分析

纯粹的大型模拟题,进行 Move to 操作时用Spfa找一下最短路。
以下举出几个易错点:

  1. 若当前行动者的 HP0 ,则不能行动;同样的若被攻击者的 HP0 ,也是为不合法。
  2. 如果一个角色被 Drive out ,那么应该及时的把他的信息删除,否则会对后续操作有影响。
  3. 一个角色被 Drive out ,那他一定不能是被 Attack ;反之,一个角色被 Attack ,那他一定不能是被 Drive out
  4. 若是同阵营的人互相攻击,则一定为不合法

Ps:若还有什么易错点可以私信我。
贴一下我巨丑无比的代码,不要嫌弃我的变量名。


#include <queue>
#include <stack>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define Max 111
using namespace std;
int x0[4]={0,1,0,-1},y0[4]={1,0,-1,0};
int Map[Max][Max],G[Max][Max],Dist[Max][Max],In[Max][Max],n,m,c,e,gr,character;
char Event[30];
queue <int> Qx,Qy;
struct Data{int HP,AT,MV,AD1,AD2,X,Y,GR;}Character[Max];
int Level(char Event[30]){
    if (Event[0]=='R') return 1;
    if (Event[0]=='A' && Event[1]=='c') return 2;
    if (Event[0]=='M') return 3;
    if (Event[0]=='D') return 4;
    return 5;
}
int CloseEnemy(int x,int y){
    return (Map[x-1][y]==1-gr) || (Map[x+1][y]==1-gr) || (Map[x][y-1]==1-gr) || (Map[x][y+1]==1-gr);
}
void Move(int Ex,int Ey){
    if (Character[character].HP<0){
        printf("INVALID\n");
        return;
    }
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++){
            Dist[i][j]=1<<30;
            In[i][j]=0;
        }
    int Sx=Character[character].X,Sy=Character[character].Y;    
    for (Qx.push(Sx),Qy.push(Sy),Dist[Sx][Sy]=0,In[Sx][Sy]=0;!Qx.empty();Qx.pop(),Qy.pop()){
        int x=Qx.front(),y=Qy.front(); In[x][y]=0;
        for (int i=0;i<4;i++){
            int xx=x+x0[i],yy=y+y0[i];
            if (xx<1 || xx>n || yy<1 || yy>m || Map[xx][yy]!=-1) continue;
            if (CloseEnemy(xx,yy)) Dist[xx][yy]=max(Character[character].MV,Dist[x][y]+G[xx][yy]);
            else if (Dist[x][y]+G[xx][yy]<Dist[xx][yy]){
                Dist[xx][yy]=Dist[x][y]+G[xx][yy];
                if (In[xx][yy] || Dist[xx][yy]>=Character[character].MV) continue;
                Qx.push(xx); Qy.push(yy); In[xx][yy]=1;
            }
        }
    }
    if (Dist[Ex][Ey]>Character[character].MV) printf("INVALID\n");
    else{
        printf("%d\n",Character[character].MV-Dist[Ex][Ey]);
        Map[Sx][Sy]=-1; Map[Ex][Ey]=gr; Character[character].X=Ex; Character[character].Y=Ey;
    }

}
void Drive(int Enemy){
    if (Character[character].HP<0){
        printf("INVALID\n");
        return;
    }
    int dist=abs(Character[character].X-Character[Enemy].X)+abs(Character[character].Y-Character[Enemy].Y);
    if (Character[Enemy].HP>0 && dist>=Character[character].AD1 && dist<=Character[character].AD2 && Character[character].AT>=Character[Enemy].HP){
        printf("%d\n",Character[Enemy].HP-=Character[character].AT);
        Map[Character[Enemy].X][Character[Enemy].Y]=-1;
    }
    else printf("INVALID\n");
}
void Attack(int Enemy){
    if (Character[character].HP<0 || Character[Enemy].GR==gr){
        printf("INVALID\n");
        return;
    }
    int dist=abs(Character[character].X-Character[Enemy].X)+abs(Character[character].Y-Character[Enemy].Y);
    if (Character[Enemy].HP>0 && dist>=Character[character].AD1 && dist<=Character[character].AD2 && Character[character].AT<Character[Enemy].HP) 
        printf("%d\n",Character[Enemy].HP-=Character[character].AT);
    else printf("INVALID\n");
}
int main(){
    //freopen("in.txt","r",stdin);
    //freopen("out.txt","w",stdout);
    scanf("%d%d%d%d",&n,&m,&c,&e);
    for (int i=1;i<=n;i++)
        for (int j=1;j<=m;j++) scanf("%d",&G[i][j]);
    memset(Map,-1,sizeof(Map));
    for (int i=1;i<=c;i++){
        scanf("%d%d%d%d%d%d%d%d\n",&Character[i].HP,&Character[i].AT,&Character[i].MV,&Character[i].AD1,&Character[i].AD2,&Character[i].X,&Character[i].Y,&Character[i].GR);
        Map[Character[i].X][Character[i].Y]=Character[i].GR;
    }
    for (int i=1;i<=e;i++){
        gets(Event);
        int level=Level(Event);
        if (level==1) gr=Event[9]-'0';
        if (level==2){
            character=0;
            for (int j=20;Event[j]>='0' && Event[j]<='9';j++) character=character*10+Event[j]-'0';
        }
        if (level==3){
            int j,Ey=0,Ex=0;
            for (j=9;Event[j]>='0' && Event[j]<='9';j++) Ex=Ex*10+Event[j]-'0';
            for (j++;Event[j]>='0' && Event[j]<='9';j++) Ey=Ey*10+Event[j]-'0';
            Move(Ex,Ey);
        }
        if (level==4){
            int Enemy=0;
            for (int j=10;Event[j]>='0' && Event[j]<='9';j++) Enemy=Enemy*10+Event[j]-'0';
            Drive(Enemy);
        }
        if (level==5){
            int Enemy=0;
            for (int j=7;Event[j]>='0' && Event[j]<='9';j++) Enemy=Enemy*10+Event[j]-'0';
            Attack(Enemy);
        }
    }
    //fclose(stdin); fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值