CodeForces 1105D (BFS)Kilani and the Game

CodeForces 1105D (BFS)Kilani and the Game

CodeForces 1105D

这是昨天Round#533 div2的D题,难度其实并不大,就是个广搜,但是有几个需要注意的地方。

首先,这题的做法是从第一个玩家开始从起始位置进行BFS,也就是题目所说的“扩张”,直到最后一个玩家“扩张”完,这样算作一轮。而每一轮中每个玩家扩张的大小,也就是BFS的深度,取决于各玩家的扩张速度 s [ i ] s[i] s[i].

于是,对于玩家数量 p p p,我们只要维护 p p p个队列。但是问题来了,如何才能控制每个玩家BFS的深度呢?我们知道从队列的意义上讲,BFS搜索一层的过程就相当于从当前队首元素出队开始到当前队尾元素出队为止,这样“旧”结点刚好全部出队,而与其邻接的“新”结点刚好全部进队。那么如何区分“新”“旧”结点呢?这时候我们就要用到一个临时队列,当原队列队首元素出队的同时将与其邻接的结点放进临时队列中而非仍然放进原队列里去,这样当原队列为空时,我们就完成了一层广度优先搜索,如果在外面再加个循环那么就达到了控制搜索深度的目的。

但是问题又来了,这个游戏会进行几轮呢?换言之,这个程序如何判断终止呢?一开始我把终止条件设为所有 p p p个队列皆为空,结果发现第5个用例运行超时,注意到题目的 s s s非常大,我查看了用例5,发现是这样一个用例:

4 4 2
1 1000000000


…2.
…1

在第一轮中,玩家2会进行1000000000次扩张,即使在第4次扩张之后的扩张毫无意义,我的程序依然会不断使玩家2进行扩张…

因此只能转换思路。其实到这里已经很明了,既然接下来的扩张无法再占有新的格子,我就设置一个增量,记录每次扩张新占有的格子数,如果该增量为0,则该次扩张无效,那么之后的扩张必然也无效,就直接break,转到下一个玩家;每一轮中再设置一个增量,记录这一轮中这 p p p个玩家新占有的格子数,如果该增量也为零,那么游戏结束,循环终止。

代码有些地方写得有点多余,懒得做修改了( ̄▽ ̄)/

#include <stdio.h>
#include <stdlib.h>
long long s[10];
char map[1005][1005];
int occu[10];
int d[4][2] = {{1, 0}, {0, 1}, {-1, 0}, {0, -1}};
//要建p个队列
struct node
{
  int x;
  int y;
  struct node *next;
  int depth;
  struct node *tail;
};
typedef struct node NODE;
NODE que[10];
void push(NODE *head, int x, int y)
{
  NODE *p = (NODE *)malloc(sizeof(NODE));
  p->x = x;
  p->y = y;
  p->next = NULL;
  head->tail->next = p;
  head->tail = p;
  head->depth++;
}
NODE *pop(NODE *head)
{
  NODE *p = head->next;
  head->next = p->next;
  head->depth--;
  return p;
}
int main(int argc, char const *argv[])
{

  int n, m, p;
  scanf("%d %d %d", &n, &m, &p);
  int i;
  for (i = 1; i <= p; i++)
  {
    que[i].tail = &que[i];
    que[i].next = NULL;
  }
  for (i = 1; i <= p; i++)
  {
    scanf("%lld", &s[i]);
  }
  getchar();
  int j;
  int newin = 0;
  for (i = 0; i < n; i++)
  {
    for (j = 0; j < m; j++)
    {
      scanf("%c", &map[i][j]);
      if (map[i][j] >= '1' && map[i][j] <= '9')
      {
        push(&que[map[i][j] - '0'], i, j);
        occu[map[i][j] - '0']++;
        newin++;
      }
    }
    getchar();
  }
  NODE *tque = (NODE *)malloc(sizeof(NODE));
  int increasep;
  int increasestep;
  while (newin > 0)
  {
    newin = 0;
    for (i = 1; i <= p; i++)
    {
      increasep = 0;
      for (j = 0; j < s[i]; j++)
      {
        increasestep = 0;
        tque->depth = 0;
        tque->tail = tque;
        tque->next = NULL;
        while (que[i].depth > 0)
        {
          NODE *p = pop(&que[i]);
          int x = p->x;
          int y = p->y;
          for (int k = 0; k < 4; k++)
          {
            int x1 = x + d[k][0];
            int y1 = y + d[k][1];
            if (x1 >= 0 && y1 >= 0 && x1 < n && y1 < m && map[x1][y1] == '.')
            {
              push(tque, x1, y1);
              increasestep++;
              occu[i]++;
              map[x1][y1] = i + '0';
            }
          }
        }
        que[i].next = tque->next;
        que[i].tail = tque->tail;
        que[i].depth = tque->depth;
        if (increasestep == 0)
        {
          break;
        }
        else
        {
          increasep += increasestep;
        }
      }
      if(increasep == 0){
        continue;
      }
      else{
        newin += increasep;
      }
    }
  }
  for (i = 1; i <= p; i++)
  {
    i == 1 ? printf("%d", occu[i]) : printf(" %d", occu[i]);
  }
  printf("\n");
  return 0;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值