POJ 2785 有多少种4个数相加等于0的方案(二分查找 or hash)


题目链接: 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Michael阿明

如果可以,请点赞留言支持我哦!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值