洛谷P1102 A-B数对的双指针和hash和(彩蛋)解法

题意很简单,就是找出有多少对满足A-B=C的A和B
一开始看哦呦这不是个普及-题么,上来就暴力,然后果然 t掉

然后开始优化:通过看题解 发现有重复数字时会有重复计算的情况,就出现了两种优化:
1、B一个个列举,找到A的重复区间,就是下面的双指针法

#include 
#include 
#include 
using namespace std;
#define MAXN 200005
typedef long long ll;
ll ans = 0;
int a[MAXN];
int r1, r2, n, c;
int main()
{
    scanf("%d %d", &n, &c);
    for(int i = 1; i <= n; i ++)
        scanf("%d", &a[i]);
    sort(a + 1, a + n + 1);
    r1 = r2 = 1;
    for(int i  = 1; i <= n; i++)
    {
        while(r1 <= n && a[r1] - a[i] <= c) r1 ++;
        while(r2 <= n && a[r2] - a[i] < c) r2 ++;
        if(r1 - 1 >= 1 && a[r1 - 1] == a[r2] && a[r2] - a[i] == c) ans += (r1 - r2);
    }
    printf("%lld\n", ans);
    return 0;
}

其实复杂度还是O(n^2)但是做了很多删减,达到了优化的效果

2、刚才的情况还是可以继续优化的,不需要在找到A的重复区间时把所有重复的A遍历一下,在读入的时候就存进去。
这样很容易想到把一样的数字归成一个类,即为“桶”的思想,但是这题数据范围太大,盲开2的32次方的数组,分分钟MLE
这时HASH开始发挥作用了,还是桶的思想,但是通过取余,相同的余数排到下一个的方法,大大缩小了桶的范围。
意思基本解释清楚了,上代码!

#include 
#include 
#include 
using namespace std;
typedef long long ll;
#define prime_num 1000003
struct node{
    int times;
    int num;
}Node[prime_num + 5];
int n, c, a[prime_num + 5];

int Hash(int x)
{
    return x % prime_num;
}

int find(int n)//找到n所在的位置,只要n不同,得出的位置一定不同
{
    int ans = Hash(abs(n));
    while(Node[ans].num && Node[ans].num != n) ans ++;
    return ans; 
}

void insert(int n)
{
    int loc = find(n);
    Node[loc].times ++;
    Node[loc].num = n;
}

int main()
{
    ll ans = 0;
    scanf("%d%d", &n, &c);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        insert(a[i]);
    }
    for(int i = 1; i <= n; i++)
    {
        ans += Node[find(a[i]-c)].times;
    }
    printf("%lld\n", ans);
    return 0;
}

3、为一个彩蛋型解法,在我写这篇题解的时候无意间想到的,改了之后发现爆了数据范围,全改成long long就过了
这个题解的思路来源于写一、二种解法时:虽然知道了重复的A的个数,但是有很多的B的情况下还是对B一个个遍历,出现了很多重复工作,例如有10000个1和10000个2,那就需要对数字1遍历10000次。
那么:
如果遇到了很多重复的B,再找到了很多重复的A,直接减不就行了吗?

然后开始着手去做,发现一个问题就是这样的话必须一个一个在hash数组中去找哪些数字是存在的,即使你只输入了两个数,但还是要对整个hash的范围进行遍历。
没简便多少。。不过也是个可行方法。

#include 
#include 
using namespace std;
typedef long long ll;
#define prime_num 1000003
struct node{
    ll times;
    ll num;
}Node[prime_num + 5];
int n, c, a[prime_num + 5];

ll LLabs(ll n)
{
    if(n >= 0) return n;
    else return n * (-1);
}

int Hash(ll x)
{
    return x % prime_num;
}

int find(ll n)//找到n所在的位置,只要n不同,得出的位置一定不同
{
    int ans = Hash(LLabs(n));
    while(Node[ans].num && Node[ans].num != n) ans ++;
    return ans; 
}

void insert(ll n)
{
    int loc = find(n);
    Node[loc].times ++;
    Node[loc].num = n;
}

int main()
{
    ll ans = 0;
    scanf("%d%d", &n, &c);
    for(int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
        insert(a[i]);
    }
    for(int i = 0; i <= prime_num; i ++)
    {
        ans += Node[i].times * Node[find(Node[i].num + c)].times;
    }
    printf("%lld\n", ans);
    return 0;
}

一个普及-的题目我花了整整一天去写它(说明我菜),但是收获还是挺大的,希望大家对所遇的题目(即使是很简单的题目)能多思考,拓宽思路,对之后的学习还是很有用滴!

你可能感兴趣的:(刷题总结)