文章目录
题目链接: http://poj.org/problem?id=2785
题目大意:给定不超过4000行4个数,每列挑出来1个,使之和为0,有多少种方案。
1.二分查找法
1.1 思路:
- 对左边两列的所有和求出来,右边两列所有的和的求出来再取负
- 右侧两列的值排序(进行二分查找)
- 对左边所有的和在右边的和中进行二分查找,并查找前后都满足要求的,计数即得答案
1.2 AC代码
/**
* @description: 求四个数相加等于0的组合种数
* @author: michael ming
* @date: 2019/5/8 22:18
* @modified by:
*/
#include <iostream>
#include <memory.h>
#include <algorithm>
using namespace std;
int a[4001], b[4001], c[4001], d[4001];
int ab[4000*4000+1], cd[4000*4000+1]; //存储a+b,c+d
int findSameValue(int value, int maxindex)//二分查找
{
int low = 0, high = maxindex, mid, index, count=0;
while(low <= high)
{
mid = low + (high - low)/2;
if(value == cd[mid]) //查找前后满足要求的个数
{
index = mid - 1;
while(index >= 0 && value == cd[index--])
count++;
index = mid + 1;
while(index <= high && value == cd[index++])
count++;
return count + 1;
}
else if(value < cd[mid])
high = mid - 1;
else
low = mid + 1;
}
return 0;
}
int main()
{
int line, k=0;
cin >> line;
memset(ab, 0, sizeof(ab));
memset(cd, 0, sizeof(cd));
for(int i = 0; i < line; ++i)
{
cin >> a[i] >> b[i] >> c[i] >> d[i];
}
for(int i = 0; i < line; ++i)
{
for(int j = 0; j < line; ++j)
{
ab[k] = a[i]+b[j];
cd[k++] = -(c[i]+d[j]);
}
}
sort(cd,cd+k); //二分查找的必要条件,有序
int result = 0;
for(int i = 0; i <= k-1; ++i)
result += findSameValue(ab[i], k-1);
cout << result << endl;
return 0;
}
2.hash查找法
2.1 思路:
- 对左边两列的所有和求出来,右边两列所有的和的求出来再取负
- 对左侧两列的和存入哈希表
- 对右边两列的和在哈希表中查找
2.2 Wrong Answer 代码
(未解决冲突,并且hash后的值可能为负,作为数组下标不行,所以错误)
/**
* @description: 4个数和为0的方案数,哈希法
* @author: michael ming
* @date: 2019/5/9 22:30
* @modified by:
*/
#include <iostream>
#include <memory.h>
using namespace std;
int a[4001], b[4001], c[4001], d[4001];
int ab[4000*4000+1], cd[4000*4000+1]; //存储a+b,c+d
int *hasht = new int[16000057];
int offset = 1000000000;
int hashfunc(int &value)
{
int mod = 16000057;
return (value%mod + value/mod)%mod;
}
int main()
{
int line, k=0, value;
cin >> line;
memset(hasht, 0, sizeof(hasht));
for(int i = 0; i < line; ++i)
{
cin >> a[i] >> b[i] >> c[i] >> d[i];
}
for(int i = 0; i < line; ++i)
{
for(int j = 0; j < line; ++j)
{
ab[k] = a[i]+b[j];
value = ab[k]+offset;
hasht[hashfunc(value)]++;
cd[k++] = -(c[i]+d[j]);
}
}
int result = 0;
for(int i = 0; i < k; ++i)
{
value = cd[i]+offset;
if(hasht[hashfunc(value)])
result += hasht[hashfunc(value)];
}
cout << result << endl;
delete [] hasht;
return 0;
}
2.3 Time Limit Exceeded 代码
/**
* @description: 4个数和为0的方案数,哈希法
* @author: michael ming
* @date: 2019/5/9 22:30
* @modified by:
*/
#include <iostream>
#include <math.h>
using namespace std;
struct linkedNode //链表节点
{
pair<int, int> data;
linkedNode *next;
linkedNode():next(NULL), data(make_pair(0,0)){}
};
class linkedList //链表
{
public:
linkedNode *head;
linkedList()
{
head = new linkedNode(); //表头哨兵
}
~linkedList()
{
delete head;
}
};
class linkedHash
{
private:
linkedList *htList; //散列表链表数组
int bucket; //散列表桶个数
public:
linkedHash(int m):bucket(m)
{
htList = new linkedList [bucket] ();
}
~linkedHash()
{
for(int i = 0; i < bucket; ++i)
{
linkedNode *p = htList[i].head->next, *q = p;
while(q != NULL)
{
p = q;
q = q->next;
delete p;
}
}
delete [] htList;
}
int hash(const int &key) const
{
return abs(key%bucket); //留余数法
}
linkedNode* find(const int &x) const
{
int i = hash(x);
linkedNode *p = htList[i].head->next, *q = htList[i].head;
while(p && p->data.first != x)
{
q = p;
p = p->next;
}
return q; //返回找到元素的前一个节点,或者没有找到,返回最后一个元素
}
linkedNode* insert(const int &x)
{
int i = hash(x);
linkedNode *p = htList[i].head, *q = p;
while(q != NULL)
{
p = q;
q = q->next;
if(q && q->data.first == x)
{
q->data.second++;
return q;
}
}
p->next = new linkedNode();
p->next->data.first = x;
p->next->data.second++;
return p->next;
}
};
int a[4001], b[4001], c[4001], d[4001];
int ab[4000*4000+1], cd[4000*4000+1]; //存储a+b,c+d
int main()
{
linkedHash ht(16000057);
int line, k=0;
cin >> line;
for(int i = 0; i < line; ++i)
{
cin >> a[i] >> b[i] >> c[i] >> d[i];
}
for(int i = 0; i < line; ++i)
{
for(int j = 0; j < line; ++j)
{
ab[k] = a[i]+b[j];
ht.insert(ab[k]);
cd[k++] = -(c[i]+d[j]);
}
}
int result = 0;
linkedNode* p;
for(int i = 0; i < k; ++i)
{
p = ht.find(cd[i])->next;
if(p && p->data.first == cd[i])
result += p->data.second;
}
cout << result << endl;
return 0;
}
2.4 偷懒失败,hash_map在poj中不存在
/**
* @description: 4个数和为0的方案数,哈希法
* @author: michael ming
* @date: 2019/5/9 22:30
* @modified by:
*/
#include <iostream>
#include <hash_map>
//#include <unordered_map>
using namespace std;
int a[4001], b[4001], c[4001], d[4001];
int ab[4000*4000+1], cd[4000*4000+1]; //存储a+b,c+d
int main()
{
__gnu_cxx::hash_map<int, int> ht;
// std::unordered_map<int, int> ht;
int line, k=0;
cin >> line;
for(int i = 0; i < line; ++i)
{
cin >> a[i] >> b[i] >> c[i] >> d[i];
}
for(int i = 0; i < line; ++i)
{
for(int j = 0; j < line; ++j)
{
ab[k] = a[i]+b[j];
ht[ab[k]]++;
cd[k++] = -(c[i]+d[j]);
}
}
int result = 0;
for(int i = 0; i < k; ++i)
{
result += ht[cd[i]];
}
cout << result << endl;
return 0;
}
2.5 哈希表+二叉查找树(超时)
可能是建立二叉树插入节点new太耗时了。
/**
* @description: 4个数和为0的方案数,哈希法
* @author: michael ming
* @date: 2019/5/9 22:30
* @modified by:
*/
#include <iostream>
#include <math.h>
using namespace std;
class BSTNode
{
public:
int data, count;
BSTNode *left, *right;
BSTNode():count(1), left(NULL), right(NULL){}
BSTNode(const int& d, BSTNode *l = NULL, BSTNode *r = NULL)
{
data = d; count = 1; left = l; right = r;
}
};
class BST
{
private:
BSTNode* root;
int nodeLen;
public:
BST():root(NULL){}
~BST(){}
void clear(BSTNode* nodeP)
{
if(nodeP == NULL)
return;
clear(nodeP->left);
clear(nodeP->right);
delete nodeP;
}
BSTNode* get_root() const { return root; }
bool isEmpty() const { return root == NULL; }
BSTNode* search(const int& d) const
{
BSTNode* p = search(d, root);
return p;
}
BSTNode* search(const int d, BSTNode* p) const
{
while(p != NULL)
{
if(d == p->data)
return p;
else if(d < p->data)
p = p->left;
else
p = p->right;
}
return NULL;
}
void insert(const int d)
{
BSTNode *p = root, *prev = NULL;
while(p != NULL)
{
prev = p;
if(d < p->data)
p = p->left;
else
p = p->right;
}
if(root == NULL)
root = new BSTNode(d);
else if(d < prev->data)
prev->left = new BSTNode(d);
else
prev->right = new BSTNode(d);
}
};
class linkedHash
{
private:
BST* ht_bstree; //散列表二叉树数组
int bucket; //散列表桶个数
public:
linkedHash(int m):bucket(m)
{
ht_bstree = new BST [bucket] ();
}
~linkedHash()
{
for(int i = 0; i < bucket; ++i)
{
ht_bstree[i].clear(ht_bstree[i].get_root());
}
delete [] ht_bstree;
}
int hash(const int &key) const
{
return abs((((key+1000000000)%bucket)+1357)%bucket); //留余数法
}
int find(const int &x) const
{
int i = hash(x);
BSTNode *p = ht_bstree[i].search(x);
if(p)
return p->count;
return 0;
}
void insert(const int x)
{
int i = hash(x);
BSTNode *p = ht_bstree[i].search(x);
if(p)
p->count++;
else
ht_bstree[i].insert(x);
}
};
int a[4001], b[4001], c[4001], d[4001];
int ab[4000*4000+1], cd[4000*4000+1]; //存储a+b,c+d
int main()
{
linkedHash ht(5000); //多次调整括号内数值,超时或者内存超限
int line, k=0;
cin >> line;
for(int i = 0; i < line; ++i)
{
cin >> a[i] >> b[i] >> c[i] >> d[i];
}
for(int i = 0; i < line; ++i)
{
for(int j = 0; j < line; ++j)
{
ab[k] = a[i]+b[j];
ht.insert(ab[k]);
cd[k++] = -(c[i]+d[j]);
}
}
int result = 0;
for(int i = 0; i < k; ++i)
{
result += ht.find(cd[i]);
}
cout << result << endl;
return 0;
}
2.6 AC代码(哈希+数组法)
/**
* @description: poj 2785 哈希法,数组实现
* @author: michael ming
* @date: 2019/5/20 18:17
* @modified by:
*/
#include <iostream>
using namespace std;
int a[4001], b[4001], c[4001], d[4001];
const int hashtablesize = 20000001;//保证一定的富裕,装载因子0.75左右
int hasht[hashtablesize];
int count[hashtablesize];
int offset = 1000000000; // 该数 > 2^29(加上他后,就没有负数了,求模后比较方便使用下标值)
int hashfunc(int value)
{
return value%hashtablesize;
}
int hashfunc_other(int value)
{
return (value+3)%hashtablesize;
}
void insert(int num)
{
int num_init = num;
num = hashfunc(num+offset);
while(hasht[num] != offset && hasht[num] != num_init)
//解决冲突,不等于初始值(够不着的大数)(值改了,位子被占了),且不等于映射的值(冲突了),第一次进入循环,第一个条件肯定不满足。
{
num = hashfunc_other(num);//冲突了,继续寻找别的下标(换一个函数,不然相同的模在这可能无限循环)
}
hasht[num] = num_init;
count[num]++;
}
int find(int num)
{
int num_init = num;
num = hashfunc(num+offset);
while(hasht[num] != offset && hasht[num] != num_init)
num = hashfunc_other(num); //往下查找空位或者相等的值得位子
if(hasht[num] == offset) //找到的是空位子,则没有匹配的和等于0
return 0;
else //找到了值相等的位子,把其对应的count位子里的个数返回
return count[num];
}
int main()
{
int line, k=0, value, i, j;
cin >> line;
for(i = 0; i < line; ++i)
{
cin >> a[i] >> b[i] >> c[i] >> d[i];
}
for(i = 0; i < hashtablesize; ++i)
hasht[i] = offset; //hash表每个元素初始化为offset
for(i = 0; i < line; ++i)
{
for(j = 0; j < line; ++j)
{
value = a[i]+b[j];
insert(value);
}
}
int result = 0;
for(i = 0; i < line; ++i)
{
for(j = 0; j < line; ++j)
{
value = (-c[i]-d[j]);
result += find(value);
}
}
cout << result << endl;
return 0;
}