一个不错的哈希题

链接:  http://www.lightoj.com/volume_showproblem.php?problem=1423

题意简化后就是:有大概30条跑道,每条跑道上长为50000,(以上都是数据的最大范围),每条跑道上有一些位置放了栏架,每条跑道的栏架个数不超过5000,

求最长的一段区间,使得这个区间内的每条跑道的栏架个数相等。


咋一看,毫无思路啊,难道说要去枚举区间长度,5000啊,还要枚举起点终点,铁定不行,然而分类里面是哈希- -,就往哈希的方向想想。

每到一个位置pos,每条跑道从0到pos的栏架数组成 了一个序列,a1 a2 a3 .. ak,那么我们要找最长的以此位置结尾的区间满足条件,就是找前面的位置中是否存在这样一个序列

b1 b2 b3 b4 ... bk,使得b[i]+d=a[i];,及每一项的差都相等,那么中间这段区间就是以当前位置结尾的最长的区间了,看看能更新答案即可。

最后一个问题,怎么快速的判断是否存在这样一个b序列呢,其实我们可以重新定义一下,如果有两个位置的序列b[i]+d=a[i] (1<=i<=k)  ,即每个数的差相等,我们就定义这是两个相等的状态,将整个序列的每个数都减去这个序列的最小值,这两个序列就变成一样的序列了,所以,便有了如下方法:将每个位置得到的序列进行哈希,每到一个位置,只需要判断这个位置的序列所表示的状态有没有被哈希过即可,没有被哈希过的话就记录当前位置为哈希映射成的值

用了两种方法哈希,一种是map<vector<int>,int>直接用map将整个序列哈希。。。

另一种是手写哈希函数,用邻接表的方法哈希

map哈希:

#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
int cnt[35][50010];
int val[35];
int main()
{
    int t,ca=1,L,K,n;
    scanf("%d",&t);
    while(t--)
    {
        map<vector<int>,int> mp;
        scanf("%d%d",&L,&K);
        memset(cnt,0,sizeof(cnt));
        for(int i=1;i<=K;i++)
        {
            scanf("%d",&n);
            for(int j=1,pos;j<=n;j++)
            {
                scanf("%d",&pos);
                cnt[i][pos]++;
            }
        }
        memset(val,0,sizeof(val));
        int ans=0;
        vector<int>v;
        for(int i=0;i<L;i++)
        {
                v.clear(); 
            int mi=100000;
            int mx=-1;
            for(int j=1;j<=K;j++)
            {
                if(cnt[j][i]) val[j]++;
                v.push_back(val[j]);
                if(mi>val[j]) mi=val[j];
                if(mx<val[j]) mx=val[j];
            }
            if(mx==mi) {ans=i+1;continue;}
            for(int j=0;j<v.size();j++)   v[j]-=mi;
            if(mp[v]) ans=max(ans,i+1-mp[v]);
            else mp[v]=i+1;
           
        }
        printf("Case %d: %d meter(s)\n",ca++,ans);
    }
    return 0;
}


邻接表:

#include<cstdio>
#include<cstring>
#include<vector>
#include<map>
#include<algorithm>
using namespace std;
int cnt[35][50010];
int val[35];
const int mod = 9973;
const int maxn = 100000;
int K;
struct NODE{
	int a[35];
	int pos;
	int next;
}edge[maxn];
int head[mod+1];
int tot;
int HASH(int num[])
{
	int tmp=0;
	for(int i=0;i<K;i++)
	{
		tmp=5000*tmp+num[i];
		tmp%=mod;
	}
	return tmp;
}
void insert(int num[],int p)
{
	int key=HASH(num);
	for(int i=0;i<K;i++) 	edge[tot].a[i]=num[i];
	edge[tot].pos=p;
	edge[tot].next=head[key];
	head[key]=tot++;
}
int find(int num[])
{
	int key=HASH(num);
	for(int i=head[key],f,j;i!=-1;i=edge[i].next)
	{
	     for(j=0;j<K;j++)
		 {
			 f=0;
			 if(num[j]!=edge[i].a[j]) break;
			 f=1;
		 }
		 if(f) return edge[i].pos;
	}
	return -1;
}
int main()
{
    int t,ca=1,L,n;
    scanf("%d",&t);
    while(t--)
    {
        memset(head,-1,sizeof(head));tot=0;
        scanf("%d%d",&L,&K);
        memset(cnt,0,sizeof(cnt));
        for(int i=1,j,pos;i<=K;i++)
        {
            scanf("%d",&n);
            for(j=1;j<=n;j++)
            {
                scanf("%d",&pos);
                cnt[i][pos]++;
            }
        }
        memset(val,0,sizeof(val));
        int ans=0;
        int v[35],cc=0,mi,mx;
        for(int i=0,j;i<L;i++)
        {
            cc=0; 
            mi=100000;
            mx=-1;
            for(j=1;j<=K;j++)
            {
                if(cnt[j][i]) val[j]++;
                v[cc++]=val[j];
                if(mi>val[j]) mi=val[j];
                if(mx<val[j]) mx=val[j];
            }
            if(mx==mi) {ans=i+1;continue;}
            for(j=0;j<cc;j++)   v[j]-=mi;
			int pos=find(v);
            if(pos!=-1) ans=max(ans,i+1-pos);
            else insert(v,i+1);
        }
        printf("Case %d: %d meter(s)\n",ca++,ans);
    }
    return 0;
}


你可能感兴趣的:(insert)