模拟赛 银河之星(即BZOJ 2719: [Violet 4]银河之星)

Time Limit: 5 Sec  Memory Limit: 128 MB

Description

Input

Output

Sample Input

2 3 3 1 1
2 2
3 3
2 3 3 1 1
1 1
2 2

Sample Output

Yes
No

HINT

题解

一个神奇的搜索。

首先对于走三格的情况,我们可以发现:一个矩阵中的所有点可以分为9个类别;每个类别中的点分别可以通过“走三格”的方法相互到达。如图:

012012
345345
678678

那么在分类的情况下,我们考虑“走两格”的情况,如0跨过1到2,我们可以将这种改变看做0和1可以合并为2,所以题目就可化为:能不将所有(最多9种)点通过一系列合并得到一个和目标格子同类的点。矩阵大小为3*3。

看这数据范围,只有搜索了。那么我们考虑怎么表示搜索状态:考虑的只有9类那么就加一个简单的hash:将所有类别的个数用一个9位的大整数表示状态,再mod一个较大的素数,怕哈希一层有问题,我用了一个很傻的链表(毕竟我很弱)。

具体的边界问题代码中看会更清楚。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>
#define ll long long
#define mod 2000007
using namespace std;
int K,n,m,xd,yd,lei,size;
struct dian {int x,y;} d[12];
int to[12][12],num[2000010],pre[2500000],last[2500000],zz;
int f[2500000],pd[2500000];
//------------------------------------------------------------------------------
void init()
{
	lei=((xd-1)%3*3+(yd-1)%3);
	int i,j,p,x,y;
	for(i=1;i<=K;i++) scanf("%d%d",&d[i].x,&d[i].y);
	memset(to,-1,sizeof(to));
	for(i=0;i<n;i++)//处理出所有可能的合并 
	for(j=0;j<m;j++)
	   {p=(i%3)*3+(j%3);
	    if(i>1&&j>1)
		   {x=((i-2)%3)*3+(j-2)%3; y=((i-1)%3)*3+(j-1)%3; to[p][y]=x;}
	    if(j>1)
		   {x=(i%3)*3+(j-2)%3; y=(i%3)*3+(j-1)%3; to[p][y]=x;}
		if(i<n-2&&j>1)
		   {x=((i+2)%3)*3+(j-2)%3; y=((i+1)%3)*3+(j-1)%3; to[p][y]=x;}
		if(i<n-2)
		   {x=((i+2)%3)*3+j%3; y=((i+1)%3)*3+j%3; to[p][y]=x;}
		if(i<n-2 && j<m-2)
		   {x=((i+2)%3)*3+(j+2)%3; y=((i+1)%3)*3+(j+1)%3; to[p][y]=x;}
		if(j<m-2)
		   {x=(i%3)*3+(j+2)%3; y=(i%3)*3+(j+1)%3; to[p][y]=x;}
		if(i>1)
		   {x=((i-2)%3)*3+j%3; y=((i-1)%3)*3+j%3; to[p][y]=x;}
	    if(i>1&&j<m-2)
		   {x=((i-2)%3)*3+(j+2)%3; y=((i-1)%3)*3+(j+1)%3; to[p][y]=x;}
	   }
}
int find(int shu)
{
	int w=shu%mod,i;
	i=last[w];
	while(i>0)
	   {if(num[i]==shu) return i;
	    i=pre[i];
	   }
	zz++;
	num[zz]=shu; pre[zz]=last[w]; last[w]=zz;
	return zz;
}
int dfs(int shu,int ct)
{
	int i,j,k,s[12],w=find(shu),now;
	if(pd[w]) return f[w];
	now=shu;
	for(i=8;i>=0;i--)
	   {s[i]=now%10; now/=10;}
	if(ct==1)//搜索的底线:当只剩一个棋子时。 
	   {pd[w]=1;
	    if(s[lei]==1) return 1;
	    else return 0;
	   }
	for(i=0;i<9;i++)
	   {if(!s[i]) continue;
	    for(j=0;j<9;j++)//常规的dfs套路。 
	       {if(s[j]&&to[i][j]!=-1)
		       {now=0;
			    s[i]--; s[j]--; s[to[i][j]]++;
			    for(k=0;k<9;k++)
			       {now=now*10; now+=s[k];}
			    if(dfs(now,ct-1))
			       {pd[w]=1;
				    f[w]=1;
				    return 1;
				   }
				s[i]++; s[j]++; s[to[i][j]]--;
			   }
		   }
	   }
	pd[w]=1;
	return 0;
}
void work()
{
	int i,j,p,shu=0,tag=1,s[12];
	memset(s,0,sizeof(s));
	memset(f,0,sizeof(f));
	memset(pd,0,sizeof(pd));
	memset(last,0,sizeof(last));
	for(i=1;i<=K;i++)
	   {p=(d[i].x-1)%3*3+(d[i].y-1)%3;
	    s[p]++;
	   }
	for(i=0;i<9;i++)//K个棋子都是同类的,永远消不掉。 
	   {if(s[i]==K) {tag=0; break;}}
	if(tag==0) {printf("No\n"); return ;}
	zz=0;
	for(i=0;i<9;i++)
	   {shu=shu*10; shu+=s[i];}
	if(dfs(shu,K)) printf("Yes\n");
	else printf("No\n");
} 
int main()
{
	freopen("galaxy.in","r",stdin);
	freopen("galaxy.out","w",stdout);
	while(scanf("%d%d%d%d%d",&K,&n,&m,&xd,&yd)!=EOF)
	   {init(); work();}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值