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