POJ3274 牛黄金平衡阵容 (哈希查找-vector模拟链表)

                                                                                    Gold Balanced Lineup

Time Limit: 2000MS Memory Limit: 65536K
Total Submissions: 17741 Accepted: 4973

Description

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 Kpossible 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

Hint

In the range from cow #3 to cow #6 (of size 4), each feature appears in exactly 2 cows in this range

思路:

Q1:知道每头牛的特征后,如何才能求最大平衡区间的奶牛?

直接想法:从 i 到 j 范围内的奶牛同一列的特征值相加相等,取 j-i+1 的最大值。

直接模拟这种思想我们需要用到三重for循环,超时是大概率情况。所以这个时候我们应该要想到使用一些算法来减小时间复杂度。这道题中我们需要用到的就是前缀和的思想。

我们令 sumi(a, b, c) 为i与i之前的牛的特征的和,sumj(e, f, g)同理(j>i)。如果 i---j 构成最大平衡区间,那么会有 sumj - sumi = (e-a, f-b, g-c) = (x, x, x)(x为正整数或0),即 sumi(a, b, c) + (x, x, x) = sumj(e, f, g) → (a+x, b+x, c+x) = (e, f, g)。统一减去第一列值后有(0, b-a, c-a) = (0, f-e, g-e)。所以这个时候我们只需要判断两个数组内相同列的值是否相等,就可以找到平衡区间。需要注意的是:当sumi内不同列的值全部相等时,就说明此时的下标 i 同样为一个平衡区间(从0位开始计算的特征值平衡)

所以步骤如下:

一、将题中所给样例数据化为二进制数(特征值从哪个方向存放其实无所谓,我们只要求 i-j 内的特征值相加相等就行)

编号    特征

①       1 1 1

②       1 1 0

③       1 1 1

④       0 1 0

⑤       0 0 1

⑥       1 0 0

⑦       0 1 0

二、这时我们让每一行的数字都与前一行的数字相加,即第 i 行 j 列的数字表示前 i 行前 j 列的值的和

编号    特征

①       1 1 1

②       2 2 1

③       3 3 2

④       3 4 2

⑤       3 4 3

⑥       4 4 3

⑦       4 5 3

三、判断是否存在sumi(a, b, c) a=b=c 的情况,如果存在,判断与maxlen(最大平衡区间)哪个更大

四、全部减去第一列(注意存在负数情况,所以我们使用哈希时key值需要取绝对值)

编号    特征

①       0 0 0

②       0 0 -1

③       0 0 -1

④       0 1 -1

⑤       0 1 0

⑥       0 0 -1

⑦       0 1 -1

五、哈希 key = (sum[][j] + ...)*4%prime   (这里我的prime取小于100000的最小素数99991,可以随意取,只要能分散开就行)

六、找到最大平衡区间

 

Q2:好像情况都考虑到了,为什么还是WA?

1. 看是否考虑到了sumi每列值相等的情况

2. 如果输入的n为1只有一头牛,那么输出不能为零,要为1 (我是没明白为什么还要这种操作,直接为零不行吗?)。如果输入的n>1超过一头牛,那么输出有可能为零(不存在平衡区间)

3. 如果还是WA,多半哈希有问题

 

Q3:不知道WA在哪怎么办?

别着急,POJ讨论区有位大神给出了部分测试数据,测试下你就知道了。

网址:http://poj.org/showmessage?message_id=346489

 

以下是我的代码:(用时891MS)

#include<cstdio>
#include<cstring>
#include<string>
#include<algorithm>
#include<iostream>
#include<vector>

using namespace std;
const int Max =100005;
const int prime = 99991;             //一般prime值取小于最大区间k倍的最大素数
int sum[Max][35];
int C[Max][35];                             //sum数组中的每一行的每一列都减去第一列的值
vector<int>Hash[Max];              //vector模拟链表
int k;
int maxlen=0;                              //最大平衡特征值

void Hash_cache(int *cc, int site){     //哈希,cc为C数组中的第i行数组,site参数的值为i
    int key = 0;
    for( int j=0; j<k; j++ ){
        key = key%prime + cc[j]<<2;     //key值取值方法(自己随意想,尽可能散列即可)
    }
    key = (key & 0x7fffffff)%prime;     //取key的绝对值(当第一列的值比后几列的数字大时)

    if( Hash[key].size()==0 ){
        Hash[key].push_back(site);
    }else{
        for( int c=0; c<Hash[key].size(); c++ ){
            for( int j=0; j<k; j++ ){
                if( cc[j]!=C[ Hash[key][c] ][j] ) break;
                if( j==k-1 && site-Hash[key][c]>maxlen )
                {
                    maxlen = site-Hash[key][c];    //因为是前缀和所以不需要+1
                }
            }
        }
        Hash[key].push_back(site);
    }
}

int main(){
    int n,x;
    int key;
    bool E=true;                         //判断是否会出现sum各值都相等
    scanf("%d %d",&n,&k);
    scanf("%d",&x);
    if( n==1 ) { printf("1\n"); return 0; }  //特殊情况:当只有一头牛时,连续 1 头牛的特征平衡
    for( int j=0; j<k; j++ ){                       //如果有两头牛以上,则可能会出现连续 0 头牛的特征平衡(即没有平衡情况)
        sum[0][j] = (x & 1);
        C[0][j] = sum[0][j] - sum[0][0];
        x>>=1;
    }
    //第一头牛直接存入sum,从第二头开始特征与前头牛相应特征相加
    for( int i=1; i<n; i++ ){
        E=true;
        scanf("%d",&x);
        for( int j=0; j<k; j++ ){
            sum[i][j] = sum[i-1][j] + (x & 1);
            C[i][j] = sum[i][j] - sum[i][0];
            if( sum[i][j]!=sum[i][0] )
            {
                E=false;
            }
            x>>=1;
        }
        if( E==true && (i+1)>maxlen ){  //平衡区间存在时
            maxlen = i+1;
        }
        Hash_cache(C[i], i);
    }
    printf("%d\n",maxlen);
    return 0;
}


/*
    1. 将特征按照二进制位存入sum[][]数组中
    2. 查看是否是平衡(当sum[i]中的各数都相同时,明显此时为平衡区间)
    3. sum所有行减去第一列
    4. 哈希
    5. 查找找到最大差距
*/

参考博客:https://blog.csdn.net/alongela/article/details/8246612

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值