poj_3274

源地址:http://poj.org/problem?id=3274

题目大意就是给你N头牛,然后给你一个特征值,比如6的二进制表示是110,说明这头牛有第二个和第三个特征,而没有第一个特征,现在题中会给一个k,表示一共有的特征数,让求一个最大的范围i~j,使得在这i~j头牛中k个特征出现的次数相同。

看了别人的思路才知道这道题目的解法。

首先,我们设一个数组sum[i][j],表示前i头牛中j特征出现的次数,那么如果想要一个区间内k个特征出现的次数相同,那么 sum[i][0]-sum[j][0] = sum[i][1] -sum[j][1] = sum[i][2] - sum[j][2] =....=sum[i][k-1]-sum[j][k-1] 其中最大的i-j就是答案。

通过变换式子,我们可以得到如下的新式子。

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][3] - sum[i][0] = sum[j][3] - sum[j][0]

.....................

那么我们再设一个数组c[i][j] = sum[i][j] - sum[i][0] (j<k),则题中要求的便化为了 c[i][] = c[j][]中最大的i-j范围

比如题中数据
7 3

7 6 7 2 1 4 2

它的sum矩阵是

1 1 1 
1 2 2 
2 3 3 
2 4 3 
3 4 3 
3 4 4 
3 5 4 

它的c矩阵是

0 0 0 
0 0 0 
0 1 1 
0 1 1 
0 2 1 
0 1 0 
0 1 1 
0 2 1 

它的第2行和第6行是相同的,所以答案为6-2=4

注意!c矩阵是有个0行的,全为0,这个一定要有,也就是说刚开始的时候k种特征出现的次数都一样,都为0.如果后面出现了全为0的一行,就得和这一行对起来。

而题目中数据很大,不能一一枚举c矩阵,那么我们就用哈希的方法来寻找,这和3349差不多。

#include<stdio.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<iomanip>
#include<vector>
#include<time.h>
#include<queue>
#include<stack>
#include<iterator>
#include<math.h>
#include<stdlib.h>
#include<limits.h>
#include<map>
//#define ONLINE_JUDGE
#define eps 1e-8
#define INF 0x7fffffff
#define FOR(i,a) for((i)=0;i<(a);(i)++)
#define MEM(a) (memset((a),0,sizeof(a)))
#define sfs(a) scanf("%s",a)
#define sf(a) scanf("%d",&a)
#define sfI(a) scanf("%I64d",&a)
#define pf(a) printf("%d\n",a)
#define pfI(a) printf("%I64d\n",a)
#define pfs(a) printf("%s\n",a)
#define sfd(a,b) scanf("%d%d",&a,&b)
#define sft(a,b,c)scanf("%d%d%d",&a,&b,&c)
#define for1(i,a,b) for(int i=(a);i<b;i++)
#define for2(i,a,b) for(int i=(a);i<=b;i++)
#define for3(i,a,b)for(int i=(b);i>=a;i--)
#define MEM1(a) memset(a,0,sizeof(a))
#define MEM2(a) memset(a,-1,sizeof(a))
const double PI=acos(-1.0);
template<class T> T gcd(T a,T b){return b?gcd(b,a%b):a;}
template<class T> T lcm(T a,T b){return a/gcd(a,b)*b;}
template<class T> inline T Min(T a,T b){return a<b?a:b;}
template<class T> inline T Max(T a,T b){return a>b?a:b;}
using namespace std;
#define ll __int64
int n,m;
#define Mod 1000000007
#define N 110
#define M 1000100
const int prime=999983;
int sum[M][35];	//sum[i][j]表示前i头牛的j特征的数量
int c[M][35];	//c[i][j] = sum[i][j] - sum[i][0]
int feature[M][35];
int k;
int len;
struct Hashtable{
	int row;
	Hashtable*next;
	Hashtable(){
		next = 0;
	}
};
Hashtable *hash[prime+5];
int getKey(int p){
	int key = 0;
	for(int j=0;j<k;j++)
		key += (c[p][j]*j);
	key = abs(key)%prime;	//哈希值有可能是负的,得取正
	return key;
}
bool cmp(int p1,int p2){
	for(int j=0;j<k;j++)
		if(c[p1][j] != c[p2][j])
			return false;
	return true;
}
void insert(int p){
	int key = getKey(p);
	if(!hash[key]){
		Hashtable *tmp = new Hashtable;
		tmp->row = p;
		hash[key] = tmp;
	}else{
		Hashtable *tmp = hash[key];
		if(cmp(tmp->row,p)){
			if(p-tmp->row>len)
				len = p-tmp->row;
			return;
		}
		while(tmp->next){
			tmp = tmp->next;
			if(cmp(tmp->row,p)){
				if(p-tmp->row>len)
					len = p-tmp->row;
				return;
			}
		}
		tmp->next = new Hashtable;
		tmp->next->row = p;
	}
	return;
}
int main(){
#ifndef ONLINE_JUDGE
    freopen("in.txt","r",stdin);
//  freopen("out.txt","w",stdout);
#endif
    	while(sfd(n,k)!=EOF){
    		for(int i=0;i<k;i++){	//初始化,即前0头牛具有的特征均为0
    			sum[0][i] = 0;
    			c[0][i] = 0;
    		}
    		int x;
    		memset(hash,0,sizeof hash);
    		insert(0);		//要放入第0头牛!
    		len = 0;
    		for(int i=1;i<=n;i++){
    			scanf("%d",&x);
    			for(int j=0;j<k;j++){
    				feature[i][j] = x%2;		//存储第i只牛是否具有j特征
    				x /= 2;
    				sum[i][j] = sum[i-1][j]+feature[i][j];
    				c[i][j] = sum[i][j] - sum[i][0];
    			}
    			insert(i);
    		}
    		printf("%d\n",len);
//    		for(int i=1;i<=n;i++){
//    			for(int j=0;j<k;j++)
//    				printf("%d ",sum[i][j]);
//    			printf("\n");
//    		}
//    		puts("");
//    		for(int i=0;i<=n;i++){
//    			for(int j=0;j<k;j++)
//    				printf("%d ",c[i][j]);
//    			printf("\n");
//    		}
    	}
return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值