NOI 3.5 哈希 1551: Sumsets

题目来源:http://noi.openjudge.cn/ch0305/1551/

1551: Sumsets

总时间限制: 1000ms    内存限制65536kB

描述

Given S, a set of integers, find the largest d such that a + b +c = d where a, b, c, and d are distinct elements of S.

输入

Several S, each consisting of a line containing an integer 1<= n <= 1000 indicating the number of elements in S, followed by theelements of S, one per line. Each element of S is a distinct integer between-536870912 and +536870911 inclusive. The last line of input contains 0.

输出

For each S, a single line containing d, or a single linecontaining "no solution".

样例输入

5
2
3
5
7
12
5
2
16
64
256
1024
0

样例输出

12
no solution

来源

Waterloo local 2001.06.02

-----------------------------------------------------

思路

如果直接枚举a,b,c再用d去二分查找,则复杂度为O(n^3*logn),会超时。故改造表达式为

a + b = d – c

分别枚举a,b和c,d,再二分查找,复杂度为O(n^2*logn)。

进一步优化,用空间换时间,开一个巨大的数组,数组长度等于a+b可能的上界-下界,二分查找的过程也免去了,复杂度为O(n^2).

但是这样空间开销太大,且这个数组会十分稀疏,因为实际上a+b的取值最多只有n*(n-1)/2种可能。故采用哈希算法。开4个数组:

1. 数组s为哈希数组,下标为哈希值,数组元素为a+b的实际值。用open addressing的方式处理collision(即发生访问冲突则线性增加索引,直到不冲突为止。open addressing适用于负载因子较低的hash table)。哈希算法采用很朴素的把a+b全部转化为非负数再对数组s大小取模

2. 数组b1下标为哈希值,数组元素为a

3. 数组b2下标为哈希值,数组元素为b. 设b1,b2两个数组主要是因为题目要求等式中a,b,c,d互不相同

4. 数组flag,下标为哈希值,true表示该哈希值的s数组有元素了,否则false. flag的意义在于在hash table中查找d-c数发现s[h]!=d-c时, 区别到底是没有hash(a+b)==h(不用找了)还是hash(a+b)==h发生了collision(需要继续往下找)。每组数据计算完毕要清空flag

编写3个函数:

1. myhash: 输入整数,计算hash

2. add: 把a+b的结果存入hash table

3. myfind: 在hash table里查找d-c的值

还有一个技巧是把输入数据排序,遍历d-c的时候按d从大到小的顺序遍历,找到的第一个d就是题目要求的最大的d

-----------------------------------------------------

代码 

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cstring>
using namespace std;

const int NMAX = 1005;				// 集合中元素最多的个数
const int MYMIN = 536870912;		// 所有输入+MYMIN可以变成非负数, %运算的时候就不会出现负数使数组下标越界	
const int RANGE = NMAX*NMAX;		// 哈希表的长度,负载因子在0.5左右
int in[NMAX] = {};					// 集合中的元素
int s[RANGE] = {};					// a+b的哈希, 用open_addressing来处理collision
bool flag[RANGE] = {};				// 哈希表中是否有数据
int b1[RANGE] = {};					// a+b中的a
int b2[RANGE] = {};					// a+b中的b

int myhash(int n)								// hash算法, 根据a+b的结果n计算hash值h
{
	int h = (n+2*MYMIN)%RANGE;
	while (flag[h])
	{
		h = (h+31)%RANGE;
	}
	return h;
}

void add(int a, int b)							// 把a+b放入哈希表s, 同时用b1,b2记录a,b
{
	int h = myhash(a+b);
	s[h] = a+b;
	b1[h] = a;
	b2[h] = b;
	flag[h] = true;
	return;
}


bool myfind(int d, int c)							// 在哈希表中查找d-c是否存在, 且a,b,c,d不能重复
{
	int h = (d-c+2*MYMIN)%RANGE;
	while (flag[h])
	{
		if (s[h]==d-c && !(b1[h]==d || b2[h]==c || b1[h]==c || b2[h]==d))
		{
			return true;
		}
		else
		{
			h = (h+31)%RANGE;
		}
	}
	return false;
}


int main()
{
#ifndef ONLINE_JUDGE
	ifstream fin ("0305_1551.txt");
	int n,i,j,a,b,c,d;
	bool is_found = false;
	while (fin >> n)
	{
		if (n==0)
		{
			break;
		}
		if (n<4)
		{
			cout << "no solution" << endl;
			continue;
		}
		memset(flag,0,sizeof(flag));		// 每轮计算过后要清空flag,否则会发生collision的误判
		is_found = false;
		for (i=0; i<n; i++)
		{
			fin >> in[i];
		}
		sort(in, in+n);						// 将集合中的元素升序排列,这样从后往前遍历d-c的时候自然就能找到最大的d
		for (i=1; i<n; i++)
		{
			for (j=0; j<i; j++)
			{
				a = in[i];
				b = in[j];
				add(a,b);
			}
		}
		for (i=n-1; i>=0; i--)
		{
			for (j=n-1; j>=0; j--)
			{
				if (i!=j)
				{
					d = in[i];
					c = in[j];
					if (myfind(d,c))
					{
						is_found = true;
						cout << d << endl;
						break;
					}
				}
			}
			if (is_found)
			{
				break;
			}
		}
		if (!is_found)
		{
			cout << "no solution" << endl;
		}
	}
	fin.close();
#endif
#ifdef ONLINE_JUDGE
	int n,i,j,a,b,c,d;
	bool is_found = false;
	while (cin >> n)
	{
		if (n==0)
		{
			break;
		}
		if (n<4)
		{
			cout << "no solution" << endl;
			continue;
		}
		memset(flag,0,sizeof(flag));
		is_found = false;
		for (i=0; i<n; i++)
		{
			cin >> in[i];
		}
		sort(in, in+n);						// 将集合中的元素升序排列,这样从后往前遍历d-c的时候自然就能找到最大的d
		for (i=1; i<n; i++)
		{
			for (j=0; j<i; j++)
			{
				a = in[i];
				b = in[j];
				add(a,b);
			}
		}
		for (i=n-1; i>=0; i--)
		{
			for (j=n-1; j>=0; j--)
			{
				if (i!=j)
				{
					d = in[i];
					c = in[j];
					if (myfind(d,c))
					{
						is_found = true;
						cout << d << endl;
						break;
					}
				}
			}
			if (is_found)
			{
				break;
			}
		}
		if (!is_found)
		{
			cout << "no solution" << endl;
		}
	}
	return 0;
#endif
}


猜你喜欢

转载自blog.csdn.net/da_kao_la/article/details/80708018
3.5