闽江学院第二届算法集训队第二阶段第一周周小测题解

文章介绍了在编程竞赛中常见的几种算法思路,包括利用二分查找解决A-B=C的问题,通过哈希表优化查找效率,以及运用前缀和解决矩阵区域和的最大值问题。此外,还涉及高精度计算的阶乘之和和找筷子问题,展示了位运算在解决特定问题上的优势。
摘要由CSDN通过智能技术生成

闽江学院第二届算法集训队第二阶段第一周周小测题解

P1102 A-B 数对

题目链接

题目背景

出题是一件痛苦的事情!

相同的题目看多了也会有审美疲劳,于是我舍弃了大家所熟悉的 A+B Problem,改用 A-B 了哈哈!

题目描述

给出一串正整数数列以及一个正整数 C C C,要求计算出所有满足 A − B = C A - B = C AB=C 的数对的个数(不同位置的数字一样的数对算不同的数对)。

输入格式

输入共两行。

第一行,两个正整数 N , C N,C N,C

第二行, N N N 个正整数,作为要求处理的那串数。

输出格式

一行,表示该串正整数中包含的满足 A − B = C A - B = C AB=C 的数对的个数。

题解

对于这题,可以使用二分哈希表算法

二分

对于 A − B = C A-B=C AB=C 可以转换为 A = B + C A=B+C A=B+C 此时只需要枚举B的所有可能然后用二分查找找到 B + C B+C B+C 的范围。

参考代码

#include <iostream>
#include <algorithm>

using namespace std;

const int N=2e5+10;
int num[N];
int main(){
    int n,c;
    long long ans=0;
    scanf("%d %d",&n,&c);
    for(int i=0;i<n;i++){
        scanf("%d",&num[i]);
    }
    sort(num,num+n);///由于数组不一定是有序的,因此要先进行一次排序
    for(int i=0;i<n;i++){
        int l=0,r=n-1;
        while(l<r){
            int mid=(l+r)>>1;
            if(num[mid]>=num[i]+c)r=mid;
            else l=mid+1;
        }
        if(num[l]==num[i]+c){
            int l2=0,r2=n-1;
            while(l2<r2){
                int mid=(l2+r2+1)>>1;
                if(num[mid]<=num[i]+c)l2=mid;
                else r2=mid-1;
            }
            ans+=l2-l+1;
        }
    }
    printf("%lld",ans);
}

哈希表

与二分的思路类似,只不过在找数的时候使用哈希表来进行查找

参考代码

#include <iostream>
#include <cstring>

using namespace std;
const int N=300007,null=0x3f3f3f3f;
int h[N],cnt[N],num[N];
//查找x在hash表中的位置
int find(int x){
    int t=(x%N+N)%N;
    while(h[t]!=null&&h[t]!=x){
        t++;
        if(t==N)t=0;
    }
    return t;
}
int main() {
    int n,c;
    long long ans=0;
    memset(h,0x3f,sizeof h);
    scanf("%d %d",&n,&c);
    for(int i=0;i<n;i++){
        scanf("%d",&num[i]);
        int x= find(num[i]);
        h[x]=num[i];
        cnt[x]++;//标记num[i]的数量
    }
    for(int i=0;i<n;i++){
        int x= find(num[i]+c);
        if(h[x]!=null){
            ans+=cnt[x];
        }
    }
    printf("%lld",ans);
}

P2004 领地选择

题目链接

题目描述

作为在虚拟世界里统帅千军万马的领袖,小 Z 认为天时、地利、人和三者是缺一不可的,所以,谨慎地选择首都的位置对于小 Z 来说是非常重要的。

首都被认为是一个占地 C × C C\times C C×C 的正方形。小 Z 希望你寻找到一个合适的位置,使得首都所占领的位置的土地价值和最高。

输入格式

第一行三个整数 N , M , C N,M,C N,M,C,表示地图的宽和长以及首都的边长。

接下来 N N N 行每行 M M M 个整数,表示了地图上每个地块的价值。价值可能为负数。

输出格式

一行两个整数 X , Y X,Y X,Y,表示首都左上角的坐标。

题解

这题考查的是前缀和的应用

先对所给的矩阵求出二维前缀和,之后再枚举所有首都可以占领的位置,最后将所有位置的值取一个最大值即可

参考代码

#include <iostream>

using namespace std;
const int N=1010;
int s[N][N];
int main(){
    int n,m,c;
    scanf("%d %d %d",&n,&m,&c);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            scanf("%d",&s[i][j]);
            s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+s[i][j];
        }
    }
    int x=0,y=0,max=-0x3f3f3f3f;//结果可能是负数所以把最大值赋值为负无穷
    for (int i = 1; i <= n-c+1; ++i) {
        for(int j = 1;j<=m-c+1;j++){
            if(s[i+c-1][j+c-1]-s[i-1][j+c-1]-s[i+c-1][j-1]+s[i-1][j-1]>max){
                max=s[i+c-1][j+c-1]-s[i-1][j+c-1]-s[i+c-1][j-1]+s[i-1][j-1];
                x=i,y=j;
            }
        }
    }
    printf("%d %d",x,y);
}

P8772 [蓝桥杯 2022 省 A] 求和

题目链接

题目描述

给定 n n n 个整数 a 1 , a 2 , ⋯   , a n a_{1}, a_{2}, \cdots, a_{n} a1,a2,,an, 求它们两两相乘再相加的和,即

S = a 1 ⋅ a 2 + a 1 ⋅ a 3 + ⋯ + a 1 ⋅ a n + a 2 ⋅ a 3 + ⋯ + a n − 2 ⋅ a n − 1 + a n − 2 ⋅ a n + a n − 1 ⋅ a n S=a_{1} \cdot a_{2}+a_{1} \cdot a_{3}+\cdots+a_{1} \cdot a_{n}+a_{2} \cdot a_{3}+\cdots+a_{n-2} \cdot a_{n-1}+a_{n-2} \cdot a_{n}+a_{n-1} \cdot a_{n} S=a1a2+a1a3++a1an+a2a3++an2an1+an2an+an1an

输入格式

输入的第一行包含一个整数 n n n

第二行包含 n n n 个整数 a 1 , a 2 , ⋯ a n a_{1}, a_{2}, \cdots a_{n} a1,a2,an

输出格式

输出一个整数 S S S,表示所求的和。请使用合适的数据类型进行运算。

题解

由于

S = a 1 ⋅ a 2 + a 1 ⋅ a 3 + ⋯ + a 1 ⋅ a n + a 2 ⋅ a 3 + ⋯ + a n − 2 ⋅ a n − 1 + a n − 2 ⋅ a n + a n − 1 ⋅ a n S=a_{1} \cdot a_{2}+a_{1} \cdot a_{3}+\cdots+a_{1} \cdot a_{n}+a_{2} \cdot a_{3}+\cdots+a_{n-2} \cdot a_{n-1}+a_{n-2} \cdot a_{n}+a_{n-1} \cdot a_{n} S=a1a2+a1a3++a1an+a2a3++an2an1+an2an+an1an
= a 1 ⋅ ( a 2 + a 3 + . . . + a n ) + a 2 ⋅ ( a 3 + a 4 + . . . + a n ) + . . . + a n − 1 ⋅ a n =a_1 \cdot (a_2+a_3+...+a_n)+a_2 \cdot (a_3+a_4+...+a_n)+...+a_{n-1} \cdot a_n =a1(a2+a3+...+an)+a2(a3+a4+...+an)+...+an1an

所以可以求出所有数的和,然后对数组进行遍历,对于每个 a i a_i ai 先让 s s s 减去 a i a_i ai 再将得出的结果乘上 a i a_i ai 最后将所有的结果相加即可。

参考代码

#include <iostream>

using namespace std;

const int N=2e5+10;
int num[N];
int main(){
    int n;
    long long sum=0,res=0;
    scanf("%d",&n);
    for(int i=0;i<n;i++){
        scanf("%d",&num[i]);
        sum+=num[i];
    }
    for(int i=0;i<n;i++){
        sum-=num[i];
        res+=sum*num[i];
    }
    printf("%lld",res);
}

P1009 [NOIP1998 普及组] 阶乘之和

题目链接

题目描述

用高精度计算出 S = 1 ! + 2 ! + 3 ! + ⋯ + n ! S = 1! + 2! + 3! + \cdots + n! S=1!+2!+3!++n! n ≤ 50 n \le 50 n50)。

其中 ! 表示阶乘,定义为 n ! = n × ( n − 1 ) × ( n − 2 ) × ⋯ × 1 n!=n\times (n-1)\times (n-2)\times \cdots \times 1 n!=n×(n1)×(n2)××1。例如, 5 ! = 5 × 4 × 3 × 2 × 1 = 120 5! = 5 \times 4 \times 3 \times 2 \times 1=120 5!=5×4×3×2×1=120

输入格式

一个正整数 n n n

输出格式

一个正整数 S S S,表示计算结果。

题解

这题考查的是高精度加法与高进度乘法的应用

这里分别给出高精度加法和乘法的模板

加法

// C = A + B, A >= 0, B >= 0
vector<int> add(vector<int> &A, vector<int> &B)
{
    if (A.size() < B.size()) return add(B, A);

    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size(); i ++ )
    {
        t += A[i];
        if (i < B.size()) t += B[i];
        C.push_back(t % 10);
        t /= 10;
    }

    if (t) C.push_back(t);
    return C;
}

乘法

// C = A * b, A >= 0, b >= 0
vector<int> mul(vector<int> &A, int b)
{
    vector<int> C;

    int t = 0;
    for (int i = 0; i < A.size() || t; i ++ )
    {
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();

    return C;
}

我们只需要套用这两个模板求出数的阶乘然后求和即可

参考代码

#include <iostream>
#include <vector>
using namespace std;

// C = A * b, A >= 0, b >= 0
vector<int> mul(vector<int> &A, int b)
{
    vector<int> C;

    int t = 0;
    for (int i = 0; i < A.size() || t; i ++ )
    {
        if (i < A.size()) t += A[i] * b;
        C.push_back(t % 10);
        t /= 10;
    }

    while (C.size() > 1 && C.back() == 0) C.pop_back();

    return C;
}

// C = A + B, A >= 0, B >= 0
vector<int> add(vector<int> &A, vector<int> &B)
{
    if (A.size() < B.size()) return add(B, A);

    vector<int> C;
    int t = 0;
    for (int i = 0; i < A.size(); i ++ )
    {
        t += A[i];
        if (i < B.size()) t += B[i];
        C.push_back(t % 10);
        t /= 10;
    }

    if (t) C.push_back(t);
    return C;
}

int main(){
    int n;
    scanf("%d",&n);
    vector<int>sum,mu;
    mu.push_back(1);
    for(int i=1;i<=n;i++){
        mu= mul(mu, i);
        sum= add(sum,mu);
    }
    for(int i=sum.size()-1;i>=0;i--){
        printf("%d",sum[i]);
    }
}

P1469 找筷子

题目链接

题目描述

经过一段时间的紧张筹备,电脑小组的“RP 餐厅”终于开业了,这天,经理 LXC 接到了一个定餐大单,可把大家乐坏了!员工们齐心协力按要求准备好了套餐正准备派送时,突然碰到一个棘手的问题:筷子!

CX 小朋友找出了餐厅中所有的筷子,但遗憾的是这些筷子长短不一,而我们都知道筷子需要长度一样的才能组成一双,更麻烦的是 CX 找出来的这些筷子数量为奇数,但是巧合的是,这些筷子中只有一只筷子是落单的,其余都成双,善良的你,可以帮 CX 找出这只落单的筷子的长度吗?

输入格式

第一行是一个整数,表示筷子的数量 n n n

第二行有 n n n 个整数,第 i i i 个整数表示第 i i i 根筷子的长度 a i a_i ai

输出格式

输出一行一个整数表示答案。

题解

这题考查的是位运算中异或和的使用

异或和:相同为0不同为1

如$10010^11010=01000

对于本题假设有3根筷子为A,A,B。对A和A求异或和得出结果为0,由于任何数与0异或都为这个数本身,所以可以通过求前缀和的方式找到单独的筷子

参考代码

#include <iostream>
using namespace std;
int main(){
    int n,x,ans=0;
    scanf("%d",&n);
    while (n--){
        scanf("%d",&x);
        ans^=x;
    }
    printf("%d",ans);
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值