BZOJ2719 - [Violet 4]银河之星 (记忆化搜索+hash)

这道题目通过细致分析,将棋子可移动的复杂状态简化为9个区域。每个区域内棋子可以相互到达,用区域左上角的格子作为代表。解决方法是利用记忆化搜索和哈希表,通过合并相邻格子的操作更新棋子数量,避免状态过多。边界处理和方向对称性的考虑是关键。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

这里写图片描述


Input

这里写图片描述


Output

这里写图片描述


Solution

一看到这题的我是懵逼的,好像有好多状态,妈妈怎么办?

然而仔细读题目,转动我们的脑子可以发现,由于每个棋子可以向各个方向移3格,且只会改变自身的位置,整个网格就被划成了9个区域:

0 1 2
3 4 5
6 7 8

其中每个区域代表的是处于某个位置的格子能走遍的所有格子的集合。举个栗子:第一行第一列的格子就跟第一行第四列的格子就在同一个集合里,而不跟第一行第二列的格子在一个集合。
而在这个集合里的任何格子间棋子可以相互到达。换句话说,处于这片区域的任何一个格子的棋子,都可以看作处于同一个格子。这样,我们就用左上角的格子作为区域的代表格,我们只用记这样3×3的一个状态。

然后跨过棋子使其消失相当于合并两个棋子到第三个格子,这在“缩略图”中,就是相邻的格子间的操作,只需要将相应区域的格子的棋子数量对应加减就行了。然后我们搜索,用hash记忆化(我用的是map),每次执行合并操作棋子数必然减少,状态不会太多。

由于方向的对称性,我们可以只记4个方向,对应要处理好边界问题,不要出界。


Code

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>
#include <cmath>
#include <iostream>
#include <map>

using namespace std;

const int dx[] = {0, 1, 1, 1};
const int dy[] = {1, -1, 0, 1};

map <long long, int> M;
int n, m, K;
int tx, ty;

int lim[3][3], val[3][3];

long long Get(){
    long long ans = 0, t = 1;
    for(int i = 0; i < 3; i++)
     for(int j = 0; j < 3; j++){
       ans += val[i][j] * t;
       t *= 11;
    }
    return ans;
}

int Find(int now){
    if(now == 1)  return val[tx][ty] ? 1 : 2;
    long long res = Get();
    if(M[res])  return M[res];

    for(int x = 0; x < 3; x++)
     for(int y = 0; y < 3; y++){
       if(!val[x][y])  continue;
       for(int i = 0; i < 4; i++){
         if(x + dx[i] >= n || y + dy[i] >= m)  continue;
         int nx = (x + dx[i] + 3) % 3, ny = (y + dy[i] + 3) % 3;
         if(!val[nx][ny])  continue;
         int nnx = (nx + dx[i] + 3) % 3, nny = (ny + dy[i] + 3) % 3;
         if(lim[nnx][nny] == val[nnx][nny])  continue;
         val[nnx][nny] ++;  val[nx][ny] --;  val[x][y] --;
         int ans = Find(now-1);
         val[nnx][nny] --;  val[nx][ny] ++;  val[x][y] ++;
         if(ans == 1)  return M[res] = 1;
       }
     }
    return M[res] = 2;
}

int main(){

    freopen("bzoj2719.in", "r", stdin);
    freopen("bzoj2719.out", "w", stdout);

    while(~ scanf("%d%d%d%d%d", &K, &n, &m, &tx, &ty)){

      M.clear();
      memset(lim, 0, sizeof(lim));
      memset(val, 0, sizeof(val));
      tx = (tx - 1) % 3;  ty = (ty - 1) % 3;

      for(int i = 0; i < n; i++)
       for(int j = 0; j < m; j++)  
         lim[i%3][j%3] ++;

      int x, y;
      for(int i = 0; i < K; i++){
        scanf("%d%d", &x, &y);
        val[(x-1)%3][(y-1)%3] ++; 
      }
      if(Find(K) == 1)  puts("Yes");
      else  puts("No");
    }
    return 0;
} 

这里写图片描述

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值