#include <string.h>
const int COW_MAX = 100000;
const int K_MAX = 31;
int cowSumInfo[100000][32];
#define BIG_PRIME 100003
struct C {
int CInfo[32];
char used;
int hval;
int cow_sequence;
};
typedef struct C C;
C CHashArray[100010];
int hash(int * CArray, int length) {
long long powSum = 0;
int i = 1;
for (; i < length; i++) {
powSum += CArray[i] * CArray[i];
}
return powSum % BIG_PRIME;
}
char CEqual(int * CArray1, int * CArray2, int length) {
int i = 1;
for (; i < length; i++) {
if (CArray1[i] != CArray2[i]) {
return 0;
}
}
return 1;
}
int fillAndGetRangeLength(int * CArray, int length, int cow_sequence) {
int hval = hash(CArray, length);
int insertPos = hval;
int arraySize = length * sizeof (int);
int longestRange = 0;
while(1) {
if (!CHashArray[insertPos].used) {
CHashArray[insertPos].cow_sequence = cow_sequence;
CHashArray[insertPos].hval = hval;
CHashArray[insertPos].used = 1;
memcpy(CHashArray[insertPos].CInfo, CArray, arraySize);
} else {
if (CHashArray[insertPos].hval == hval) {
if (CEqual(CArray, CHashArray[insertPos].CInfo, length)) {
return cow_sequence - CHashArray[insertPos].cow_sequence;
}
}
if (++insertPos >= BIG_PRIME + 1) {
insertPos = 0;
}
}
}
}
int checkLongest(int cow_num, int K) {
int longest = 0;
int C[32] = {0};
fillAndGetRangeLength(C, K, 0);
int i = 1;
for (; i <= cow_num; i++) {
int j = 1;
int rangeLength = 0;
C[0] = 0;
for (; j < K; j++) {
C[j] = cowSumInfo[i][j] - cowSumInfo[i][0];
// printf("%d ", C[j]);
}
// printf("\n");
rangeLength = fillAndGetRangeLength(C, K, i);
longest = longest >= rangeLength ? longest : rangeLength;
}
printf("%d\n", longest);
return longest;
}
void buildcowSumInfo(int cow_sequence, int cow_feature, int K) {
int cur_feature = 1;
int i = 0;
for (; i < K; i++) {
int increment = 0;
if (cur_feature & cow_feature) {
if (!cow_sequence) {
cowSumInfo[cow_sequence][i]++;
} else {
cowSumInfo[cow_sequence][i] = cowSumInfo[cow_sequence-1][i] + 1;
}
} else {
if (!cow_sequence) {
cowSumInfo[cow_sequence][i] = 0;
} else {
cowSumInfo[cow_sequence][i] = cowSumInfo[cow_sequence-1][i];
}
}
cur_feature = cur_feature<<1;
}
// for (int i = 0; i < K; i++) {
// printf("%d ", cowSumInfo[cow_sequence][i]);
// }
// printf("\n");
}
int main() {
int K;
int cow_num;
while(scanf("%d", &cow_num) > 0) {
int i = 0;
scanf("%d", &K);
memset(CHashArray, 0, sizeof(CHashArray));
memset(cowSumInfo, 0, sizeof(cowSumInfo));
for (;i < cow_num; i++) {
int cow_feature;
scanf("%d", &cow_feature);
buildcowSumInfo(i+1, cow_feature, K);
}
checkLongest(cow_num, K);
}
}
C++(GCC WA.....) 26384K 532MS
这道题的难点其实已经不在hash了, 而是如何转化的问题, 刚开始看题,没都懂,后来查了查才清楚:
一个人养了N头牛, 而牛们则一共有K种特性: 每种有自己的编号, 比如feature1 就是牛身上有斑点, feature2就是牛更喜欢pascal(尼玛, 黑程序员...),
每头牛的特性总和可以用一个数来表示: 比如 7 = 0111(二进制), 那么这头牛就有特性 1 ,2 ,3 (跟二进制每一位有关).
题目让求的其实就是在这一群牛里, 从第i 牛 到 第 j 牛中,拥有每种特性的牛的数量都是一样的:
一个例子:
7 3
7 (111)
6 (110)
7 (111)
2 (010)
1 (001)
4 (100)
2 (010)
一共7头牛,共3种特性, 那么可以看到 从第3头牛 到 第6头牛, 分别拥有这3种特性的牛的数量是相等的(都是2)。
这就是一个黄金平衡牛群,要找的就是最大的黄金平衡牛群,输出其大小即可(这个例子就是 4, 从3~ 6共4头).
接下来就是如何转化成程序可以搞定的问题了, 貌似这道题还是一道经典题,很多兄弟也都直接参考了官方解题报告:
先这样定义一个数据:sum[i][k] : 从开始到第i头牛,拥有特性k 的牛的数量总和:
那么题目要求的牛群(i~j, 共K种特性)就满足这样的条件:
sum[j][1] - sum[i-1][1] == sum[j][2] - sum[i-1][2] == .... == sum[j][K] - sum[i-1][K], 即对于每种特性, 第 j头到第i头牛中, 拥有此特性的牛的数量都相等.
注意是i-1 而不是 i(因为牛群把第i头算进去了, 因此要比较, 必须与i-1比较)
这个数组很好得到, 不过有个比较关键的一点是, 对于sum[0][k] 要置为0(0头牛,自然特性数都是0), sum数组的第一组数据应该是全0, 当时漏了这个考虑.
这种情况就出错了:
4 4
1
2
4
8
从上面的等式条件,可以进一步变形:
sum[j][2] - sum[j][1] == sum[i-1][2] - sum[i-1][1]
sum[j][3] - sum[j][1] == sum[i-1][3] - sum[i-1][1]
sum[j][4] - sum[j][1] == sum[i-1][4] - sum[i-1][1]
。。。。。。。。。。。。。。。。。。。。
sum[j][k] - sum[j][1] == sum[i-1][k] - sum[i-1][1]
。。。。。。。。。。。。。。。。。。。。
sum[j][K] - sum[j][1] == sum[i-1][K] - sum[i-1][1]
证明很简单,由上面每个式子都能得到 sum[j][k] - sum[i-1][k] = sum[j][1] - sum[i-1][1] 等式右边是个定值.
到这一步, 就再定义C[i][k] = sum[i][k] - sum[i][1], 那么对于某个牛群(从i到 j), 满足黄金平衡的条件就是:
对于任意的k 1 <=k <= K, C[i-1][k] = C[j][k], 即 sum[i-1][k] - sum[i-1][1] = sum[j][k] - sum[j][1] => sum[j][k] - sum[i-1][k] = sum[j][1] - sum[i-1][1], 再由每个k都满足,即可得到
sum[j][k] - sum[i-1][k]都是相等的, 满足了黄金平衡.
, 现在黄金平衡条件变成了两个一维数组(C[i] 和 C[j])完全相同.
接下来才开始用hash, 因为要验证数组是否相等, 为了提高效率, 就把数组 整个 都hash, 然后遍历 C[i] 数组(i 从第一头到第N头), 找到匹配成功的, 记下牛群的大小(这需要hash数组也能保存在此位置保存的C数组 是从第几头牛开始计算 得到的, 这样才能算出牛群的大小, 比如 当前是第j , 而匹配的是 第i,那么牛群大小就是 j - i, 因为是从前向后,因此不会有遗漏),与之前进行比较,
如果大就更新
一直到遍历完成, 输出这个过程中最大的牛群数量.
这道题的核心思想不是hash, 而应该是前缀数组和.