贪心+枚举/哈希表 HDOJ Trouble

题目传送门

题意:5个集合,每个集合最多200个数字,问是否每个集合挑一个数加起来和为0。

分析:显然n^5的程序果断超时,甚至n^3logn的二分也过不了。想n^3的方法,既然判断有没有,那么可以将两个两个的集合合并成两个大集合,再枚举最后一个集合,两个大集合排完序之后一个指针从开头最小开始,另一个从最大的开始,>0,大指针往左移,<0,小指针往右移,那么可以在线性时间求解,这贪心方法很巧妙!

另一种做法算是暴力+优化了,哈希表储存一个两个集合合并后大集合的数字,n^3暴力询问是否哈希表内存在它的相反数,哈希表用到链式前向星,总算有点理解了,贴张图:

收获:1. ”微调“贪心方法 2. 哈希表 + 链式前向星

代码1(贪心):

/************************************************
 * Author        :Running_Time
 * Created Time  :2015-8-26 9:07:01
 * File Name     :HDOJ_4334.cpp
 ************************************************/

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <bitset>
#include <cstdlib>
#include <ctime>
using namespace std;

#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
typedef long long ll;
const int N = 2e2 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
ll a[5][N];
ll sum[2][N*N];

int main(void)    {
    int T; scanf ("%d", &T);
    while (T--) {
        int n;  scanf ("%d", &n);
        for (int i=0; i<5; ++i) {
            for (int j=0; j<n; ++j) {
                scanf ("%I64d", &a[i][j]);
            }
        }
        int tot = 0;
        for (int i=0; i<n; ++i) {
            for (int j=0; j<n; ++j) {
                sum[0][tot] = a[0][i] + a[1][j];
                sum[1][tot++] = a[2][i] + a[3][j];
            }
        }
        sort (sum[0], sum[0]+tot);
        sort (sum[1], sum[1]+tot);
        int cnt1 = 1, cnt2 = 1;
        for (int i=1; i<tot; ++i) {                                     //其实离散化没什么优化
            if (sum[0][i] != sum[0][i-1])   sum[0][cnt1++] = sum[0][i];
            if (sum[1][i] != sum[1][i-1])   sum[1][cnt2++] = sum[1][i];
        }

        bool flag = false;
        for (int i=0; i<n && !flag; ++i) {
            for (int j=0,k=cnt2-1; j<cnt1 && k>=0; )  {
                ll tmp = a[4][i] + sum[0][j] + sum[1][k];
                if (tmp == 0)   {
                    flag = true;    break;
                }
                else if (tmp < 0)   j++;
                else    k--;
            }
        }

        printf ("%s
", flag ? "Yes" : "No");
    }

    return 0;
}

代码2(哈希表):

/************************************************
* Author        :Running_Time
* Created Time  :2015-8-26 18:21:55
* File Name     :D_2.cpp
 ************************************************/

#include <cstdio>
#include <algorithm>
#include <iostream>
#include <sstream>
#include <cstring>
#include <cmath>
#include <string>
#include <vector>
#include <queue>
#include <deque>
#include <stack>
#include <list>
#include <map>
#include <set>
#include <bitset>
#include <cstdlib>
#include <ctime>
using namespace std;

#define lson l, mid, rt << 1
#define rson mid + 1, r, rt << 1 | 1
typedef long long ll;
const int N = 2e2 + 10;
const int INF = 0x3f3f3f3f;
const int MOD = 1e6 + 7;
struct Hash_Table   {
    struct Edge {
        ll x;
        int nex;
    }edge[MOD];
    int head[MOD], E;
    void init(void) {
        memset (head, -1, sizeof (head));
        E = 0;
    }
    void insert(ll x)   {
        int u = (x % MOD + MOD) % MOD;
        for (int i=head[u]; ~i; i=edge[i].nex)  {
            if (edge[i].x == x) return ;
        }
        edge[E].x = x;  edge[E].nex = head[u];
        head[u] = E++;
    }
    bool find(ll x) {
        int u = (x % MOD + MOD) % MOD;
        for (int i=head[u]; ~i; i=edge[i].nex)  {
            if (edge[i].x == x) return true;
        }
        return false;
    }
}ha;
ll a[5][N];

int main(void)    {
    int T;  scanf ("%d", &T);
    while (T--) {
        int n;  scanf ("%d", &n);
        for (int i=0; i<5; ++i) {
            for (int j=0; j<n; ++j) scanf ("%I64d", &a[i][j]);
        }
        ha.init ();
        for (int i=0; i<n; ++i) {
            for (int j=0; j<n; ++j) {
                ha.insert (a[0][i] + a[1][j]);
            }
        }
        bool flag = false;
        for (int i=0; i<n && !flag; ++i) {
            for (int j=0; j<n && !flag; ++j) {
                for (int k=0; k<n; ++k) {
                    if (ha.find (-(a[2][i] + a[3][j] + a[4][k])))   {
                        flag = true;    break;
                    }
                }
            }
        }
        puts (flag ? "Yes" : "No");
    }

    return 0;
}

  

编译人生,运行世界!
原文地址:https://www.cnblogs.com/Running-Time/p/4761394.html