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;
}