HDU 6046 hash(搜索标识)

来源:互联网 发布:如何把linux改为英语 编辑:程序博客网 时间:2024/06/09 16:55

hash

Time Limit: 10000/5000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)
Total Submission(s): 122    Accepted Submission(s): 33


Problem Description
Qscqesze is busy at data cleaning.
One day,he generates a large matrix by Jenkins one-at-a-time hash:
inline unsigned sfr(unsigned h, unsigned x) {
  return h >> x;
}
int f(LL i, LL j) {
  LL w = i * 1000000ll + j;
  int h = 0;
  for(int k = 0; k < 5; ++k) {
    h += (int) ((w >> (8 * k)) & 255);
    h += (h << 10);
    h ^= sfr(h, 6);
  }
  h += h << 3;
  h ^= sfr(h, 11);
  h += h << 15;
  return sfr(h, 27) & 1;
}
Obviously,it's a 1e6*1e6 matrix.The data is at row i column j is f(i,j).Note that i and j are both numbered from 1.
Then he gets some matrices sized 1e3*1e3 from the matrix above.But he forgets their original postion.Can you help him to find them out?You just are asked to tell Qscqesze the left-top corner's postion.
 

Input
The first line is the number of test cases T (T<=3). 
Here come with T cases.Each case is consist of 1000 0/1-strings sized 1000.
For convenience,the sample input is 10*10.And the real testcase is 1e3*1e3.
 

Output
For each test case, output a single line "Case #x :y z", where x is the case number, starting from 1. And y z is the answer.
 

Sample Input
10000011100000011001101111111000011110010011010101010010010010100111110111100101000111011101100110100
 

Sample Output
Case #1 :123456 234567
 

Source
2017 Multi-University Training Contest - Team 2 
 

Recommend
liuyiding   |   We have carefully selected several similar problems for you:  6055 6054 6053 6052 6051 


题目大意:

    给你一个1e6*1e6的bool矩阵的生成方法,每次给你一个1e3*1e3的bool矩阵,问这个矩阵在大矩阵中的位置。


解题思路:

    首先这个矩阵的生成方式是没有任何规律的,可以认为是随机的。大矩阵非常大,即没办法快速的生成也存不下。由于unsigned long long能保存的状态数远大于1e6*1e6,所以基本上可以认为每个位置的标识矩阵都是不同的,所以我们可以在大矩阵中找一些小矩阵作为标识,小矩阵匹配之后再在这个位置匹配整个输入矩阵。

    由于unsigned long long能存64位二进制数,我们就可以以8*8的小矩阵作为标识矩阵。接下来就是隔多远取一个标记的问题了,我们可以根据鸽笼原理知道当标记的间隔小于输入矩阵的边长-2*标记矩阵的边长时,至少一定有一个标识矩阵在输入矩阵中。那么我们就取980作为标识间隔。

    接下来就是代码实现方式了。先在大矩阵中每隔980行列,取一个8*8的小矩阵,状压到一个unsigned long long中,用hashmap记录位置,然后对于输入矩阵枚举8*8小矩阵的左上角,判断这个矩阵是否是标识矩阵,如果是的话,就可以计算出输入矩阵的左上角坐标,利用这个坐标把输入矩阵与大矩阵匹配,如果匹配成功则这个坐标就是答案。

    关于这样写的时间复杂度。首先预处理出标识矩阵的时间复杂度为:(1e6)/980*(1e6)/980*8*8,对输入矩阵枚举标识矩阵并匹配的时间复杂度为:(1e3)*(1e3)*8*8。由于基本上可以认为每个位置的标识矩阵都是不同的,所以基本上可以认为最终检测只有一次,时间复杂度为(1e3)*(1e3)。所以总时间复杂度为O(7*10^7),题目限时5s,就可以过了。


AC代码:

#include <iostream>#include <algorithm>#include <cstdio>#include <cstring>#include <cstdlib>#include <cmath>#include <ctime>#include <vector>#include <queue>#include <stack>#include <deque>#include <string>#include <map>#include <set>#include <list>using namespace std;#define INF 0x3f3f3f3f#define LL long long#define ULL unsigned long long#define fi first#define se second#define mem(a,b) memset((a),(b),sizeof(a))const int MAXL=1000000;const int MAXN=1000;char s[MAXN+2][MAXN+2];//输入矩阵struct HashMap{    const static int MOD=1196473;    int head[MOD],next[2*MAXN*MAXN],cnt,y[2*MAXN*MAXN],x[2*MAXN*MAXN];    ULL val[2*MAXN*MAXN];    inline void insert(ULL _val,int _y,int _x)    {        int p=_val%MOD;        ++cnt;        val[cnt]=_val;        y[cnt]=_y;        x[cnt]=_x;        next[cnt]=head[p];        head[p]=cnt;    }    pair<int,int> find(ULL _val)    {        int p=_val%MOD;        for(int i=head[p];i;i=next[i])            if(val[i]==_val)                return make_pair(y[i], x[i]);        return make_pair(0, 0);    }}save;//保存标识矩阵的状压值和坐标inline unsigned sfr(unsigned h, unsigned x){     return h >> x; } int f(LL i, LL j){     LL w = i * 1000000ll + j;     int h = 0;     for(int k = 0; k < 5; ++k) {         h += (int) ((w >> (8 * k)) & 255);         h += (h << 10);         h ^= sfr(h, 6);     }     h += h << 3;     h ^= sfr(h, 11);     h += h << 15;     return sfr(h, 27) & 1; } bool judge(int y,int x)//最终判断{    for(int i=0;i<MAXN;++i)        for(int j=0;j<MAXN;++j)            if(s[i][j]-'0'!=f(y+i, x+j))                return false;    return true;}int main(){    for(int y=1;y<=MAXL;y+=980)//预处理出标识矩阵        for(int x=1;x<=MAXL;x+=980)        {            ULL res=0;            for(int i=0;i<8;++i)                for(int j=0;j<8;++j)                    res=(res<<1)|f(y+i, x+j);            save.insert(res, y, x);        }    int T_T;    scanf("%d",&T_T);    for(int cas=1;cas<=T_T;++cas)    {        int ansy=0,ansx=0;        for(int i=0;i<MAXN;++i)            scanf("%s",s[i]);        for(int y=0;y+7<MAXN;++y)        {            for(int x=0;x+7<MAXN;++x)            {                ULL res=0;                for(int i=0;i<8;++i)                    for(int j=0;j<8;++j)                        res=(res<<1)|(s[y+i][x+j]=='1');                pair<int, int> p=save.find(res);                if(p.fi&&judge(p.fi-y, p.se-x))//找到答案                {                    ansy=p.fi-y;                    ansx=p.se-x;                    break;                }            }            if(ansy)                break;        }        printf("Case #%d :%d %d\n",cas,ansy,ansx);    }        return 0;}

原创粉丝点击