【计算思维作业】D.数对选择

题目

给你一个长度为n的数组和一个正整数k,问从数组中任选两个数使其和是k的倍数,有多少种选法。 对于数组a1=1 , a2=2 , a3=2而言:
(a1,a2)和(a2,a1)被认为是同一种选法;
(a1,a2)和(a1,a3)被认为是不同的选法。

输入数据

第一行有两个正整数n,k。n<=1000000,k<=1000000 第二行有n个正整数,每个数的大小不超过1e9

输出数据

选出一对数使其和是k的倍数的选法个数

样例输入
5 6
1 2 3 4 5
样例输出
2
样例说明

样例解释:
a1+a5=6,a2+a4=6,都是6的倍数
所以符合条件的选法有(1,5),(2,4)

思路

初版思路(显示RE/问了下后也可能是TLE)

一开始我的思路是,设置两个数组nums和items。

nums储存某个数字出现的个数,items储存所有不同的数字。

扫描一遍样例输入中第二行的数组,其中,nums里某个特定的数字的下标与items里面该下标对应的出现次数是相同的。

落实到代码就RE了,报错看不懂一点,这个思路不行。

二版思路(AC)

(a_{1}+a_{2})\%k=(a_{1}\%k+a_{2}\%k)\%k

有了这个公式,我们就可以将样例输入第二行所有千奇百怪的数字全部映射到[0,k-1]以内。

再取俩相加取k模,得到的结果与没有映射相同。

所以可以用一个大小为k的数组,下标对应的取模后的结果,元素代表出现次数。

这样将取模次数相乘就得到数对个数了。

什么时候取k模为0?

(1)a_{1}\%k=0 && a_{2}\%k=0

(2)k\%2=0 时,a_{1}\%k=\frac{k}{2} && a_{2}\%k=\frac{k}{2} 

(3)a_{1}\%k=i && a_{2}\%k=k-i

注意,当a_{1}\%k=a_{2}\%k 时,是数组的同一个下标的元素相乘,但题目要求的是选取两个不同的数。 

如果数组一个下标的元素直接乘自己,就相当于把数对的两个位置都是自己的情况记了两次,但题目说明(a_{1},a_{2})(a_{2},a_{1})是同一种选法。所以相当于多记了一次。

因此,对于a_{1}\%k=a_{2}\%k 的情况,数对个数是C_{n}^{2} ,也就是n选2进行组合。

也要注意,当k为奇数和k为偶数,取模后相同的次数是不一样的,奇1偶2。

当然可以将k为奇数和偶数分开讨论,不过有更聪明的循环方式,不用刻意区分k是奇是偶。

代码

//1:不是最优版
#include<iostream>
#include<vector>
using namespace std;
int main()
{
    int n,k,tmp,count,j,half;
    count=0;
    cin>>n>>k;
    vector<int> vec(k,0);
    for(int i=0;i<n;i++)
    {
        cin>>tmp;
        vec[tmp%k]+=1;
    }
    count+=vec[0]*(vec[0]-1)/2;
    if(k%2==0){
        half=k/2;
        count+=vec[half]*(vec[half]-1)/2;
    }
    else half=k/2+1;
    for(int i=1;i<half;i++)
    {
        count+=vec[i]*vec[k-i];
    }
    cout<<count<<endl;
    return 0;
}
//2.最简单版本
#include<iostream>
#include<vector>
using namespace std;
int main()
{
    int n,k,tmp,count,j;
    count=0;
    cin>>n>>k;
    vector<int> vec(k,0);
    for(int i=0;i<n;i++)
    {
        cin>>tmp;
        vec[tmp%k]+=1;
    }
    for(int i=0;i<k;i++)
    {
        j=(k-i)%k;
        if(j<i)break;
        else if(j>i)
            count+=vec[i]*vec[j];
        else
            count+=vec[i]*(vec[i]-1)/2;
    }
    cout<<count<<endl;
    return 0;
}

在最优版代码里,设置了变量j,一般思路是j=(k-i)但再对k取模后,但i=0时,j可以与i同时为0。

所以只需要再判断i和j的大小关系就可以了。

当然在j>i时相当于整个数组已经遍历完全,break结束程序。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值