1.Question:
2.Solution:
首先我们通过范例来了解一下题意是什么:
7 3
1 1 1
1 1 0
0 1 0
0 0 1
1 0 0
0 1 0
如代码所示,我们k代表的是二进制的位数,n代表的是奶牛个数
注意这种排列是题目要求的唯一的,我们不能改动
最后,我们开始转化问题
首先,我们想要快速的求出差值最好的方法就是就是对数组从头到尾进行累加(这种方法也是非常的优秀)但是本题只用这一种思想是远远不够的
我们想要求最长的满足要求的区间长度的话,还需要对问题进行转化
因为如果我们用朴素的方法的话需要至少O(n^2)的比较次数,这在数据量是10w的时候是无法容忍的,所以我们想要才去跟家高效的查找方式就是哈希
但是我们怎么哈希呢
首先,我们对题意进行转化
sum[i][j]存储第i行的之前的累计的值
carray[i][j]存储每一行的sum[i][j]-sum[i][0]
sum[i][1]-sum[i][0] = sum[j][1]-sum[j][0]
sum[i][2]-sum[i][0] = sum[j][2]-sum[j][0]
......
sum[i][k-1]-sum[i][0] = sum[j][k-1]-sum[j][0]
令C[i][y]=sum[i][y]-sum[i][0] (0<y<k)
初始条件C[0][0~k-1]=0
这里我们就将查找的条件转化成看carray数组的对应位是否相同了(其实不做最后一步转化也可以只不过我们要计算罢了)
最后我们用哈希来高效存储提高查找效率,不断更新我们的最大值就好了
在这里我们总结哈希的本质就是
原本我们需要对每个元素都要重新遍历一遍数组找最大值,但是我们现在通过哈希省去了很大一部分的不必要的判断,我们先明确必要条件然后我们去查询减少了我们的判断的次数
提高时间效率
PS:这里要小心最重要的一点,carray数组的第一个元素是从0开始的,不是从1开始的,不然对于
4 4
1
2
4
8
这种是会出错的,我就是WA在这里了
3.Code:
#include"iostream"
#include"cstdio"
#include"cstring"
#include"cmath"
#include"cstdlib"
#define N 1000000 //100w的散列
#define MAX 100005 //10w的数据量
using namespace std;
typedef struct node
{
int x;
struct node* next;
}point;
int n,k;
int sum[MAX][32];
int carray[MAX][32];
point hash[N]; //散列记录下标
int hash_function(int i)
{
int p=0;
for(int j=0;j<k;j++)
{
p=((p+(carray[i][j])<<2))%N;
}
return abs(p)%N;
}
void init()
{
for(int i=0;i<N;i++) hash[i].next=NULL;
point* w=new point; //这里就是WA的核心
w->next=hash[0].next;
hash[0].next=w;
w->x=0;
}
int main()
{
init();
int result=0;
int x;
scanf("%d%d",&n,&k);
for(int i=1;i<=n;i++)
{
scanf("%d",&x);
for(int j=0;j<k;j++) //问题转化
{
sum[i][j]=sum[i-1][j]+(x & 1);
carray[i][j]=sum[i][j]-sum[i][0];
x=x>>1; //x是正数
}
int key=hash_function(i); //将carray的第i行哈希,返回散列地址
point* w=hash[key].next;
while(w!=NULL)
{
int j;
for(j=0;j<k;j++)
{
if(carray[i][j]!=carray[w->x][j]) break;
}
if(j==k&&result<i-w->x) result=i-w->x;
w=w->next;
}
point* h=new point;
h->x=i;
h->next=hash[key].next;
hash[key].next=h;
}
printf("%d\n",result);
return 0;
}