第 2 届河北省大学生程序设计竞赛(河北省赛)-Problem K. Bitmap-题解

传送门

Problem A. Mex Query
Problem B. Nim Game
Problem C. icebound 的账单
Problem G. 520
Problem H. 神殿
Problem J. icebound 的商店
Problem K. Bitmap
       哈希讲解
       二维哈希讲解
Problem L. 跑图





Problem K. Bitmap

Time Limit: 1000ms
Memory Limit: 262144KB

Description

RSYJ is a computer scientist. He has developed many useful image search tools. But now he has encountered some problems.

We use a matrix of H × H H \times H H×H to represent a bitmap with H × H H \times H H×H size, and each pixel of the 8 8 8-bit bitmap is represented by the integer between [ 0 , 255 ] [0,255] [0,255].

Now, RSYJ have a 8 8 8-bit bitmap A A A with n × n n \times n n×n size, and a 8 8 8-bit bitmap B B B with m × m m \times m m×m size.RSYJ uses an image processing software to copy bitmap B B B to some positions in bitmap A A A. Due to RSYJ’s computer’s error, the value of each pixel in the bitmap B B B is added with an offset k k k , which is an integer, but RSYJ doesn’t know what k k k is.

Now your task is writing a program to help RSYJ find all positions of bitmap B B B in the bitmap A A A. To simplify the problem, you only need output how many positions of bitmap B B B in bitmap A A A.

For example, here are two bitmaps A A A and B B B:

AB
10 9 3
11 6 5
15 7 2
4 3
5 0

Bitmap B B B was added with an offset: 6 6 6. It becomes:

10 9

11 6

Bitmap B B B was added with an offset: 2 2 2. It becomes:

6 5

7 2

So there are two positions of bitmap B B B in bitmap A A A.

Input

The first line of the input gives two positive integers n , m n , m n,m, representing the size of bitmap A A A and the size of bitmap B B B, respectively.

The next nn lines give the bitmap A A A . Each line contains nn integers.

The next mm lines give the bitmap B B B . Each line contains mm integers.

1 ≤ n ≤ 400 1 \leq n \leq 400 1n400,

1 ≤ m ≤ 200 1 \leq m \leq 200 1m200,

0 ≤ a i j ≤ 255 0 \leq a_{ij} \leq 255 0aij255,

0 ≤ b i j ≤ 255 0 \leq b_{ij} \leq 255 0bij255

Output

Please output an integer, representing the number of positions of bitmap B B B in bitmap A A A.

Sample Input

3 2
1 2 9
3 4 7
5 6 0
3 4
5 6

Sample Output

2

题目大意

给你两个矩阵,一个是大矩阵,一个是小矩阵。
矩阵中值的范围必须是0 ~ 255,你把小矩阵中的每个值都加上或减去一个相同的数(还要保证每个数范围都是0 ~ 255),看看能不能在大矩阵中找到小矩阵。
尝试给小矩阵加上所有可以加的数,看看能在大矩阵中找到多少小矩阵。


解题思路

这是一道二维哈希题。
如果您还不会哈希,请先到这里 → \rightarrow 哈希
如果您还不会二维哈希,请再到这里 → \rightarrow 二维哈希

好了,请允许我认为你已经会了(二维)哈希 ( q w q qwq qwq)。

那么这一题,在给出矩阵A后,我们就可以求出A中所有的大小为B的子矩阵的哈希值,并记录下来(下标过大,可以使用map来记录)。
之后计算出矩阵B的哈希值,同时记录下来B的最大值和最小值,以便确定B的偏移量的范围。
如果每次都真的把B中的每一个数都加上一个偏移量,再重新计算B的哈希,那么复杂度是 O ( 255 × m 2 ) O(255\times m^2) O(255×m2),此题可以通过。
但是我们想到哈希的意义,可以理解为 b a s e base base进制下的数。我们先拿大家最熟悉的 10 10 10进制来举例:

假如矩阵B是 5   7   4   2 5\ 7\ 4\ 2 5 7 4 2,那么十进制下矩阵B的哈希就是 5742 5742 5742
把B中的每一个元素 + 1 +1 +1 6853 6 8 5 3 6853,那么哈希值为 6853 6853 6853
再加一得 7964 7964 7964
不难发现 7964 − 6853 = 1111 , 6853 − 5742 = 1111 7964-6853=1111,6853-5742=1111 79646853=1111,68535742=1111。因此我们知道原来矩阵B的哈希值 5742 5742 5742后,就不需要每次都把B中的每个元素 + 1 +1 +1,只需要每次把B的哈希值 + 1111 +1111 +1111即可。
那么 1111 1111 1111是怎么来的呢? 1111 = 1 + 1 ∗ 10 + 1 ∗ 10 ∗ 10 + 1 ∗ 10 ∗ 10 ∗ 10 1111=1+1*10+1*10*10+1*10*10*10 1111=1+110+11010+1101010,其中 10 10 10 10 10 10进制。

那么换成 p p p进制的话,我们可以先算出来那个“1111”(记为 o n c e once once),方法是: o n c e = ∑ i = 0 m ∗ m p i once=\sum_{i=0}^{m*m}p^i once=i=0mmpi
因此,我们得到B的初始的哈希后,只需要每次都加上或减去 o n c e once once即可。


另外,这题在 p = 131 p=131 p=131 u n s i g n e d   l o n g   l o n g unsigned\ long\ long unsigned long long自然溢出即可通过。

emmm,如果还不太懂,代码还有一些注释:

AC代码

#include<bits/stdc++.h>
#include<map>
using namespace std;
typedef unsigned long long ull;
const int N=400;//矩阵A的范围
const int M=200;//矩阵B的范围
const int p=131;//131进制
ull power[M*N+5];//power[i]代表p的i次方
ull A[N+5][N+5];//矩阵A的行哈希
ull getLine(int x,int l,int r)//第i行从l到r的哈希
{
    return A[x][r]-A[x][l-1]*power[r-l+1];
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)//涉及到前缀的一般从1开始
    {
        A[i][0]=0;
        for(int j=1;j<=n;j++)
        {
            int t;
            scanf("%d",&t);
            A[i][j]=A[i][j-1]*p+t;//这个数的行哈希=左边的行哈希*进制+这个数
        }
    }
    power[0]=1;//131^0=1
    for(int i=1;i<=m*m;i++)//最多用到131^(m*m)
    {
        power[i]=power[i-1]*p;//不用取模,自然溢出即可(对2^64取模)
    }
    map<ull,int>Map;//用来记录A中一个大小为B的矩阵的哈希出现过哪几次
    for(int i=m;i<=n;i++)//先求列
    {
        ull s=0;//初始值是0
        int r=i,l=r-m+1;//从l到r
        for(int j=1;j<=n;j++)
        {
            s=s*power[m]+getLine(j,l,r);//上一行l~r的行哈希*p^m加上这一行l~r的行哈希
            if(j>m)s=s-getLine(j-m,l,r)*power[m*m];//如果这是第m+1行及更大,就需要减去多余B矩阵的那一行
            if(j>=m)Map[s]++;//如果这个矩阵的范围已经达到了B矩阵的范围,记录下来
        }
    }
    //B:
    ull s=0;//B矩阵的初始哈希
    int mb=256,Mb=0;//B矩阵中的最小值(初始值为256)、最大值(初始值为0)
    for(int i=1;i<=m;i++)
    {
        for(int j=1;j<=m;j++)
        {
            int t;
            scanf("%d",&t);//输入B矩阵
            mb=min(mb,t);//更新最小值
            Mb=max(Mb,t);//和最大值
            s=s*p+t;//哈希=前面的哈希*p+这个数
        }
    }
    ull once=0;//B中的所有元素都变化1时,B矩阵总的哈希值的变化
    for(int i=0;i<m*m;i++)
    {
        once+=power[i];
    }
    int Jian=mb,Jia=255-Mb;//要减去的次数、要加上的次数
    int ans=0;//答案
    ull tempS=s;//拷贝一份B矩阵的原始哈希
    for(int i=1;i<Jian;i++)//这次从1开始,没有计算原始B矩阵的哈希
    {
        tempS-=once;//每次-once
        ans+=Map[tempS];
    }
    tempS=s;//再拷贝一份
    for(int i=0;i<=Jia;i++)//这次从0开始,计算了B矩阵的原始的值
    {
        ans+=Map[tempS];//因为含有原始的值,所以先更新ans再+once
        tempS+=once;//每次+once
    }
    cout<<ans<<endl;
    return 0;
}

原创不易,转载请附上原文链接哦~
Tisfy:https://letmefly.blog.csdn.net/article/details/116538925

评论 18
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Tisfy

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值