[BZOJ1033]杀蚂蚁Antbuster - 最全面坑点剖析

啊,大模拟~

我知道你们最想看的是坑点总结

搜集了几位神犇总结的坑点1,加上我自己发现的一些:

  1. 大视野上题面无排版,建议看CodeVS上的题面。同时大视野上的样例解释有误(那个样例解释对应的是codeVS上的样例)。
  2. 输出中的“年龄”比描述中少1。
    要么在输出时减1;要么改为刚出生的蚂蚁年龄是0,行动方式变化的蚂蚁(年龄+1)%5==0而不是年龄%5==0。
  3. “不能是蚂蚁上一秒所在的点”指的是上一秒来时的方向。
  4. 选择方向的过程是:首先根据规则1-3选出一个最优方向,这个时候判断如果秒数不是5的倍数的话就直接走过去;如果是5的倍数就按照下一个规则逆时针转,直到“可达”,然后走过去。
    注意可达点的定义以及各种前提(先可达、再信息素最大) 。题面中,可选择的方向 == 可以去的方向 != 可达的点。前两者要求信息素最大,而后者不要求。
  5. 杀死一只蚂蚁之后,需要更新:撤销占用地图,当前蚂蚁数量,蛋糕
  6. 蚂蚁用链表维护最好,这样自动按出生顺序排序。但我懒得写链表,就用的vector+erase(),也很方便。直接用构造函数和析构函数实现蚂蚁的出生和死亡,在vector中存指针,可以保证不出现重复的对象或意外产生复制构造或析构的情况。
  7. 实现“炮塔同时瞄准和开火”,只要瞄准和计算伤害时并不考虑蚂蚁是否已死,在所有laser攻击完之后,再统一erase掉死蚂蚁就好。
  8. 蚂蚁直径为 1 ,半径为 0.5
  9. 计算蚂蚁是否被激光打中时容易被卡精度。这里又一种用int而避免使用double的做法:
    设laser在 P 点,target在 Q 点,ant在 A 点,判断 A 是否被线段 PQ 打中。首先算 A PQ 的距离,这可以通过 PA PQ 法向量方向上的投影计算。直接将 PQ 旋转 π2 得到法向量,于是点击变成叉积,法向量模长等于 |PQ| 。于是
    |PA×PQ||PQ|12

    两边平方并移项得
    4|PA×PQ|2|PQ|2

    这样两边都是整数。且由于坐标 8 ,两边都可以用int直接存下。整数比较就不存在精度问题了。
    然后再判断 A 是否超出线段端点,可以通过 PA PQ 方向上的投影计算。
    0PAPQ|PQ||PQ|

    0PAPQ|PQ|2

    这样两边也都是整数,可以无误差计算。

然后就自己码大模拟吧。WA了就把题从头到尾再仔细读一遍,看是否哪里理解错了。

代码

#include 
#include 
#include 
using namespace std;

//basic operations with positions & vectors
struct Vector {
    int x, y;
    inline bool legal() const;
    template <typename Tp>
        inline Tp& operator [] (Tp array[10][10]) const {return array[x][y];}   //call map[x][y] by pos[map]
    inline Vector operator + (const Vector &v) const {return (Vector){x+v.x, y+v.y};}
    inline Vector operator - (const Vector &v) const {return (Vector){x-v.x, y-v.y};}
    inline bool operator == (const Vector &v) const {return x == v.x && y == v.y;}
    inline bool operator != (const Vector &v) const {return x != v.x || y != v.y;}
    inline int operator * (const Vector &v) const {return x*v.x + y*v.y;}
    inline int operator ^ (const Vector &v) const {return x*v.y - y*v.x;}
    inline void operator += (const Vector &v) {x += v.x; y += v.y; }
    inline int len() const {return x * x + y * y;}  //return len^2
    inline bool online(const Vector &p, const Vector &q) const {    //dis to line(p,q) <= 0.5
        Vector v = q - p, u = operator-(p);
        int l1 = u ^ v, l2 = u * v, lv = v.len();   //avoid using double
        return 4*l1*l1 <= lv && l2 >= 0 && l2 <= lv;
    }
};
const Vector dir[] = {{0,1},{1,0},{0,-1},{-1,0}};   //4 dirs in clockwise order

struct Ant {
    Vector pos, last;
    int age, level, health, healthMax;
    //Needn't to remember if carrying cake. Check (cake == this) instead

    inline Ant();   //born
    inline void leavePhero();
    void movePos();
    void takeCake();
    inline ~Ant(); //die
};

struct Laser {
    Vector pos;
    void shoot();
};

//GROUND
int N, M;
bool ocpy[10][10];  //occupied by ant or laser
int phero[10][10];  //pheromone

inline bool Vector::legal() const { //within ground && not occupied
    return x>=0 && x<=N && y>=0 && y<=M && !ocpy[x][y];
}

void losePhero() {  //phero-- if exist
    for (int x = 0; x <= N; x++)
        for (int y = 0; y <= M; y++)
            if (phero[x][y]) phero[x][y]--;
}

//ANT
vector ants;  //living ants
int totAnt = 0;     //tot ants ever existed
Ant *cake = NULL;   //point to ant who carries cake
typedef vector::iterator It;  //loops are more convenient with C++11

//born at home          //NOTICE age=0 not same as description
inline Ant::Ant(): age(0), level(totAnt++ / 6 + 1) {
    pos.x = pos.y = 0;
    pos[ocpy] = true;
    last = pos;
    health = healthMax = 4 * pow(1.1, level);
}

//leave pheromone on ground
inline void Ant::leavePhero() { pos[phero] += cake == this ? 5 : 2; }

//Choose a direction & Move its way
void Ant::movePos() {
    static int freeDir[4];  //accessible directions
    int cnt = 0, best, val = -1;
    for (int i = 0; i < 4; i++) {   //++i to turn clockwise
        Vector nx = pos + dir[i];
        if (nx.legal() && nx != last) {
            if (nx[phero] > val) {
                val = nx[phero];
                best = cnt;
            }
            freeDir[cnt++] = i;
        }
    }
    last = pos;
    if (!cnt) return;   //no way accessible
    if (age % 5 == 4)   //NOTICE not same as description
        if (best) best--;   //--i to turn counterclockwise
        else best = cnt - 1;
    pos[ocpy] = false;
    pos += dir[freeDir[best]];
    pos[ocpy] = true;
}

//try to carry cake. Assume cake is on ground
void Ant::takeCake() {
    if (pos.x < N || pos.y < M) return; //must at cake's pos
    cake = this;    //become target of all lasers
    if ((health += healthMax/2) > healthMax)
        health = healthMax;
}

//die
inline Ant::~Ant() {
    if (cake == this) cake = NULL;  //set cake back to ground
    pos[ocpy] = false;
}

//LASER
int totLaser;
Laser lasers[22];
int damage, range;  //range = radius^2

//Aim & Shoot
void Laser::shoot() {
    Vector tar; //target pos
    if (cake && (cake->pos - pos).len() <= range)
        tar = cake->pos;    //aim at cake if possible
    else {
        int nearest = range + 1;    //find nearest target
        for (It i = ants.begin(); i != ants.end(); i++) {
            int dis = ((*i)->pos - pos).len();
            if (dis < nearest) {
                nearest = dis;
                tar = (*i)->pos;
            }
        }
        if (nearest > range) return;    //out of range
    }
    for (It i = ants.begin(); i != ants.end(); i++) //hurt every ant online
        if ((*i)->pos.online(pos, tar)) (*i)->health -= damage;
}

//GENERAL MANAGING
int main() {
    scanf("%d%d%d%d%d",&N,&M,&totLaser,&damage,&range);
    range *= range; //range = radius^2
    for (int i = 0; i < totLaser; i++) {
        scanf("%d%d",&lasers[i].pos.x,&lasers[i].pos.y);
        lasers[i].pos[ocpy] = true;
    }
    int t, T;
    scanf("%d",&T);
    bool gameOver = false;
    for (t = 1; t <= T; t++) {
        //born new ant if (ants less than 6 && home is free)
        if (ants.size() < 6 && !ocpy[0][0]) ants.push_back(new Ant);    //this calls Ant::Ant()
        //leave pheromone & move & try to take cake
        for (It i = ants.begin(); i != ants.end(); i++) {
            (*i)->leavePhero();
            (*i)->movePos();
            if (!cake) (*i)->takeCake();
        }
        //lasers shoot. regardless if ant is alive
        for (int i = 0; i < totLaser; i++)
            lasers[i].shoot();
        //clear out dead ants
        for (It i = ants.begin(); i != ants.end(); )
            if ((*i)->health < 0) {
                delete *i;  //this calls Ant::~Ant()
                i = ants.erase(i);
            }
            else i++;
        //GG if cake arrives home
        if (cake && !cake->pos.x && !cake->pos.y) {
            gameOver = true;
            break;
        }
        //ground lose pheromone & ants get older
        losePhero();
        for (It i = ants.begin(); i != ants.end(); i++)
            (*i)->age++;
    }
    if (gameOver) printf("Game over after %d seconds\n",t);
    else puts("The game is going on");
    printf("%llu\n",ants.size());
    for (It i = ants.begin(); i != ants.end(); i++)
        printf("%d %d %d %d %d\n",(*i)->age,(*i)->level,(*i)->health,(*i)->pos.x,(*i)->pos.y);
    return 0;
}

  1. 感谢 Hzoi_joker 和 Clove_unique ↩

你可能感兴趣的:(总结,题解)