POJ 3274——Gold Balanced Lineup(题意转化+哈希)

题目链接https://vjudge.net/contest/369436#problem/H
Farmer John’s N cows (1 ≤ N ≤ 100,000) share many similarities. In fact, FJ has been able to narrow down the list of features shared by his cows to a list of only K different features (1 ≤ K ≤ 30). For example, cows exhibiting feature #1 might have spots, cows exhibiting feature #2 might prefer C to Pascal, and so on.

FJ has even devised a concise way to describe each cow in terms of its “feature ID”, a single K-bit integer whose binary representation tells us the set of features exhibited by the cow. As an example, suppose a cow has feature ID = 13. Since 13 written in binary is 1101, this means our cow exhibits features 1, 3, and 4 (reading right to left), but not feature 2. More generally, we find a 1 in the 2^(i-1) place if a cow exhibits feature i.

Always the sensitive fellow, FJ lined up cows 1…N in a long row and noticed that certain ranges of cows are somewhat “balanced” in terms of the features the exhibit. A contiguous range of cows i…j is balanced if each of the K possible features is exhibited by the same number of cows in the range. FJ is curious as to the size of the largest balanced range of cows. See if you can determine it.
Input
Line 1: Two space-separated integers, N and K.
Lines 2… N+1: Line i+1 contains a single K-bit integer specifying the features present in cow i. The least-significant bit of this integer is 1 if the cow exhibits feature #1, and the most-significant bit is 1 if the cow exhibits feature # K.
Output
Line 1: A single integer giving the size of the largest contiguous balanced group of cows.
Sample Input

7 3
7
6
7
2
1
4
2

Sample Output

4

翻译
n头牛,总共有k种属性。
输入n个数,表示每头牛的特性标识符。将每个数转化为长度为k的二进制,二进制第i位(从右向左)为1则意味着这头奶牛有第i个属性,0则意味着没有这个属性。
比如一头奶牛的特性标识符为13,13转化为二进制1101,从右向左看,这头奶牛拥有1 3 4号特性,但没有2号特性。
求,一个最大子序列长度,子序列满足每个属性出现的次数和都相等。

111
110
111
010
001
100

010
从3号牛到6号牛,每个属性都出现2次。

分析:

1. 引入sum二维数组

sum[i][j]:从第1头牛到第i头牛,属性j出现的次数。

对于某一个区间,所有属性出现的次数都相等,用sum数组可以表示为:

sum[i][1]-sum[j][1]=sum[i][2]-sum[j][2]=…=sum[i][k]-sum[j][k](j<i)
求最大的区间的长度,即求max(i-j)

2. 在sum数组的基础上,进行变形

sum[i][2]-sum[i][1]=sum[j][2]-sum[j][1]
sum[i][3]-sum[i][1]=sum[j][3]-sum[j][1]

sum[i][k]-sum[i][1]=sum[j][k]-sum[j][1] (第1项都等于第二项,第三项,,第k项)

3. 引入c二维数组代替sum数组

c[i][x]=sum[i][x]-sum[i][1] (1<x<k)
表示,从第1头牛到第i头牛,属性x出现的次数减去从第1头牛到第i头牛,属性1出现的次数。

对于2中的sun变形,每一行可以写成c[i][]=c[j][],对于k个属性,都需要满足,即二维数组c[][]的第i行与第j行对应的列的值都相等。
即,求相等的两行且距离最远的i-j。

4. 哈希优化

N最大100000,所以不能暴力枚举判断两行相等,我们考虑用哈希,将每一行的c[i][]相加作为它的哈希值。若两行的哈希值相等,再去比较它们的每一列是否相等(即解决哈希冲突)

代码:

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
const int N=1e5+10;
const int mod=1e6+3;
vector<int>stk[mod];
int n,k;
int sum[N][35],c[N][35];///sum[i][j]:从第1头牛到第i头牛,属性j出现的次数
int bit[35],tot;        ///c[i][j]=sum[i][j]-sum[i][1]
void binary(int x)
{
    tot=0;
    memset(bit,0,sizeof(bit));
    while(x)///转化为二进制
    {
        bit[++tot]=x%2;
        x/=2;
    }
}
int main()
{
    scanf("%d %d",&n,&k);
    for(int i=1; i<=n; i++)
    {
        int dex;
        scanf("%d",&dex);
        binary(dex);
        for(int j=1; j<=k; j++)
            sum[i][j]=sum[i-1][j]+bit[j];
    }
    for(int i=1; i<=n; i++)
        for(int j=1; j<=k; j++)
            c[i][j]=sum[i][j]-sum[i][1];
    int mx=0,sum;
    for(int i=0; i<=n; i++)
    {
        sum=0;
        for(int j=1; j<=k; j++)
            sum+=c[i][j];
        if(sum<0)///避免负数
            sum=-sum;
        sum%=mod;
        for(int j=0; j<stk[sum].size(); j++)///sum相同,即哈希值相等,比较这一行的每一个元素,更新最远距离
        {
            int y=stk[sum][j],index=1;
            while(index<=k&&c[i][index]==c[y][index])
                index++;
            if(index>k&&mx<(i-y))
                mx=i-y;
        }
        stk[sum].push_back(i);///用关键字sum代替每一行
    }
    printf("%d\n",mx);
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zaiyang遇见

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值