题目链接http://www.lydsy.com/JudgeOnline/problem.php?id=1567
Description
Blue Mary最近迷上了玩Starcraft(星际争霸) 的RPG游戏。她正在设法寻找更多的战役地图以进一步提高自己的水平。 由于Blue Mary的技术已经达到了一定的高度,因此,对于用同一种打法能够通过的战役地图,她只需要玩一张,她就能了解这一类战役的打法,然后她就没有兴趣再玩儿这一类地图了。而网上流传的地图有很多都是属于同一种打法,因此Blue Mary需要你写一个程序,来帮助她判断哪些地图是属于同一类的。 具体来说,Blue Mary已经将战役地图编码为n*n的矩阵,矩阵的每个格子里面是一个32位(有符号)正整数。对于两个矩阵,他们的相似程度定义为他们的最大公共正方形矩阵的边长。两个矩阵的相似程度越大,这两张战役地图就越有可能是属于同一类的。
Input
第一行包含一个正整数n。 以下n行,每行包含n个正整数,表示第一张战役地图的代表矩阵。 再以下n行,每行包含n个正整数,表示第二张战役地图的代表矩阵。
Output
仅包含一行。这一行仅有一个正整数,表示这两个矩阵的相似程度。
Sample Input
3
1 2 3
4 5 6
7 8 9
5 6 7
8 9 1
2 3 4
Sample Output
2
HINT
样例解释:
子矩阵:
5 6
8 9
为两个地图的最大公共矩阵
约定:
n
≤
50
题目大意:给出俩 n×n ( n≤50 )的矩阵,求他俩最大公共矩阵(还得是正方形)的边长
二分矩阵长度
mid
,将每个
mid×mid
的矩阵Hash一下…
我用的是自然溢出+链表查找(第一次尝试自然溢出Hash代码太丑轻喷QWQ)
代码如下:
#include<cstring>
#include<cstdio>
#define MOD 2333333
using namespace std;
int abs(long long a){return a>0?a:-a;}
int n,a[51][51],b[51][51],s[2505],l,r,mid,ans,top,len;
bool d[MOD+5];
int fir[MOD+5];
int Hash(){
long long sum=2332136544ll;
for(int i=1;i<=len*len;i++){
sum=sum*535345646+1ll*s[i]*s[i]*5311320233+2336516546537ll;//自然溢出Hash
}
if(sum<0) sum=-sum;
return int(sum%MOD);
}
struct Edge{
int a[2505],nex;
void fz(){
for(int i=1;i<=len*len;i++)
a[i]=s[i];
}
}nex[2505];//链表
void add(int k){
nex[++top].fz();
nex[top].nex=fir[k];
fir[k]=top;
}
bool pd(int k){
for(int i=1;i<=len*len;i++)
if(nex[k].a[i]!=s[i]) return false;
return true;
}
bool Find(int k){
for(int i=fir[k];i;i=nex[i].nex)
if(pd(i)) return true;
return false;
}
bool check(int k){
len=k;top=0;
memset(fir,0,sizeof fir);
memset(d,false,sizeof d);
memset(s,0,sizeof s);
for(int i=1;i<=n-k+1;i++)
for(int j=1;j<=n-k+1;j++){
int cnt=0;
for(int ii=i;ii<=i+k-1;ii++)
for(int jj=j;jj<=j+k-1;jj++)
s[++cnt]=a[ii][jj];//将每个矩阵转化成序列来Hash(因为是正方形,不用考虑形态)
add(Hash());
}
for(int i=1;i<=n-k+1;i++)
for(int j=1;j<=n-k+1;j++){
int cnt=0;
for(int ii=i;ii<=i+k-1;ii++)
for(int jj=j;jj<=j+k-1;jj++)
s[++cnt]=b[ii][jj];
if(Find(Hash())) return true;
}
return false;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&a[i][j]);
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
scanf("%d",&b[i][j]);
l=1;r=n;
while(l<=r){//二分
mid=(l+r)>>1;
if(check(mid)) {ans=mid;l=mid+1;}
else r=mid-1;
}
printf("%d",ans);
return 0;
}