BZOJ[1567][JSOI2008]Blue Mary的战役地图 二分+Hash

题目链接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 ( n50 )的矩阵,求他俩最大公共矩阵(还得是正方形)的边长

二分矩阵长度 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值