連結: 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;}