Loongint的旅行安排 基础的乱搞,哈希或二分

今天第二次模拟赛,题目仍然是很基础的,比如这道,即是乱搞。写这道题主要练习了一下哈希最基础的应用:模大质数。(当然这道题可以不用哈希来写)
题目链接:http://oi.nks.edu.cn/showproblem?problem_id=1982

这道题如果暴力枚举是O(n^3)的,而如果我们想办法把它变成O(n^2)即可。
思路就是:我们可以维护每两个数的和,O(n^2)做到;之后再枚举第三个数j和答案i,i要逆序,判断两个数的和中是否存在i-j,O(n^2)做到。
但要实现,是有许多问题需要注意的:
1.我们要判断i-j是否存在,所以维护的每两个数的和只需要标记一下即可,但是由于数据大小我们并开不了这么大的bool型数组。有两种解决方法:把所有的和排序,二分查找i-j是否存在;用哈希,模一个大质数后,就可以开得下数组了。为了更快我选的后者。
2.记录和的同时还要记录是哪两个数的和,因为这两个数不能和i,j重合,这里用结构体比较方便。
3.用哈希必然会出现冲突,所以我们需要用到链表。这样的话用结构体就更加方便,其中只需要三个变量,表示两个数,以及链表指针(数组模拟)指向的下一个元素。
4.对于负数的情况,需要再加一个我们所模的大质数让它变成正数,C语言没有负下标,这样顶多也就是可能多几个链。
5.由于这个哈希本质是同余,所以会出现一些错误的情况,只需要加一句特判即可。(如四个数5,7,9,8,5+7+9>8,但是在mod13下,5+7+9=8)所加的特判就是,在哈希之后的数判断存在i-j(也是需要mod P的)这个数存在后,取出组成这个和的那两个数,用他们原来的值加和判断是否等于i-j原来的值即可。

小吐槽一下:在网上搜题解搜到了两份,一份是pascal写的哈希的,并看不懂,写C的总觉得pascal的很乱,然而它过不了;还有一份是C语言,用二分写的,仍然过不了,应该是出了以上的小问题。自己也算是巩固了一下哈希的应用以及细节的处理。

#include <cstdio>
#include <algorithm>
#include <cstring>
#define P 1000003 
using namespace std;

int n, a[1001], hash[1000005];

struct node{
    int a, b, nex;
}t[1000005];
int d;

bool jud(int q, int a, int b){
    return t[q].a == a || t[q].b == a || t[q].b == a || t[q].b == b;
}

int main()
{
    int N = 2;
    while(N--){
        d = 0;
        memset(hash, 0, sizeof hash);
        memset(t, 0, sizeof t);
        scanf("%d", &n);
        for(int i = 1; i <= n; i++) scanf("%d", a+i);
        sort(a+1, a+n+1);
        for(int i = 1; i < n; i++)
            for(int j = i+1; j <= n; j++){
                int k = (a[i]+a[j]) % P;
                if(k < 0) k += P;
                if(!hash[k]) {
                    hash[k] = ++d;
                    t[d].a = i, t[d].b = j; 
                }else{
                    d++;
                    int q = hash[k];
                    while(t[q].nex) q = t[q].nex;
                    t[q].nex = d;
                    t[d].a = i, t[d].b = j;
                }
            }

        int ans = n, flag = 1;
        for(; ans && flag; ans--)
            for(int i = n; i; i--){
                if(i == ans) continue;
                int k = (a[ans]-a[i]) % P;
                if(k < 0) k += P;
                if(!hash[k]) continue;
                int q = hash[k];
                while(jud(q, i, ans) && t[q].nex) q = t[q].nex;
                if(!jud(q, i, ans) && q){
                    if(a[t[q].a] + a[t[q].b] + a[i] != a[ans]) continue;
                    flag = 0;
                    printf("%d\n", a[ans]);
                    break;
                }
            }
        if(flag) puts("No Solution");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值