链接: 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;
}