C程序训练:两个数组按规则结合形成一个集合

发布时间:2024年01月15日

题目描述:?小花有一个数组A,小树有一个数组B。小花和小树的关系很好,他们希望合并手中的数组,得到新的集合C={a+b|a∈A, b∈B}。

输入格式:第一行输入两个整数N,M,分别表示数组A,B的长度。第二行包含N个整数,表示数组A。第三行包含M个整数,表示数组B。

? (0 ≤ N,M ≤ 2 *10^5, 0 ≤ A[i],B[i] ≤ 2 * 10^6)

输出格式:输出一个整数,表示C的元素个数。

输入样例:

10 10

12 14 0 2 2 15 26 17 8 44

1 4 6 10 22 13 19 50 39 0

输出样例:

56

注意:本题时间限制较严格,由于暴力解法时间复杂度为O(n^2),可能无法满足本题要求,因此请尽可能优化你的算法( 6000ms,512MB)。

分析:我们首先想到的算法就是暴力解法,用双重循环实现。源程序如下:


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
   int n, m;
   scanf("%d %d", &n, &m);

   int* arrl=(int*)malloc(sizeof(int) * n);
   int* arr2=(int*)malloc(sizeof(int) * n);
   if (arrl==NULL || arr2 == NULL) return 1;  
   for (int i = 0; i < n; ++i)  {
      scanf("%d",&arrl[i]);
   }
   for(int i = 0; i< m; ++i) {
     scanf("%d",&arr2[i]);
   }
   char* check =(char*)malloc(sizeof(char) * 4000001);
   if (check == NULL) return 1;
   memset (check, 0, sizeof(char) * 4000001);

   int count= 0;
   for (int i = 0; i < n; ++i) {
      for (int j = 0; j < m; ++j)  {
         check[arrl[i] + arr2[j]]=1;    
      }
   }
   for(int i=0; i< 4000001; i++){
      count += check[i];
   }
   printf("%d\n",count);
   return 0;
}

暴力算法虽然简单,但数据量大时,运行时间会超时,因为算法的时间复杂度为O(n^2),无法满足题目要求。因此,必须找到一种解决方案。因此有网友提出一种方案,使用傅里叶变换实现。

设A数组[0, 1, 3, 5, 6]中的元素可看成多项式(1+x+x^3+x^5+x^6)的系数,B数组中[2,3,4]中的元素可看成多项式(x^2+x^3+x^4)的系数。计算这两个多项式的乘法(1+x+x^3+x^5+x^6)*(x^2+x^3+x^4),然后看结果中哪些项前面有系数,统计有系数项的个数,即为所求答案。

使用快速傅里叶变换进行多项式乘法运算,其时间复杂度为O(nlogn),应能满足题目要求。源程序如下。


#include <stdio.h>
#define LLONG long long
#define MAXN  (1<<23)
#define mod 998244353 //质数,在编程中常被用来做模数
int id[MAXN];
int a[ MAXN],b[ MAXN];
int quickpow(int x, int b)
{
  LLONG ans=1,t=x;
  while(b){
    if(b&1) ans=ans*t%mod;
    t=t*t%mod;
    b>>=1;
  }
  return ans;
}
int init(int len)
{
  int lim=1;
  while(lim<=len)  {
    lim<<=1;
  }
  for(int i=1; i<lim; i++){
    id[i] = (id[i>>1]>>1)+(i&1)*(lim>>1);
  }
  return lim;
}
void NTT(int a[],int lim, int opt)
{
  for(int i=1; i<lim; i++){
    if(i<id[i])  {
      int step = a[i];
      a[i] = a[id[i]];
      a[id[i]] = step;
    }
  }
  for(int len=2; len<=lim; len<<=1)  {
    int k=len>>1;
    int wn=quickpow(3,(mod-1)/len);
    for(int i=0; i<lim; i+=len){
      LLONG g=1;
      for(int j=0; j<k; j++,g=g*wn%mod){
        int temp=a[i+j+k]*g%mod;
        a[i+j+k]=(mod-temp+a[i+j])%mod;
        a[i+j]=(a[i+j]+temp)%mod;
      }
    }
  }
  if(opt==-1)  {    
    for (int i = 0; i < lim; i++){
      int temp = a[i+1];
      a[i+1] = a[lim - i];
      a[lim - i] = temp;
    }
    LLONG inv=quickpow(lim,mod-2);
    for(int i=0; i<lim; i++)  {
      a[i]=a[i]*inv%mod;
    }
  }
  return;
}
int main()
{
  int n,m,lim=0;
  int x,i;
  scanf("%d%d",&n,&m);
  for(i=1; i<=n; i++)  {
    scanf("%d",&x);
    a[x]=1;
    if(x>lim)lim=x;
  }
  for(i=1; i<=m; i++)  {
    scanf("%d",&x);
    b[x]=1;
    if(x>lim)lim=x;
  }
  lim = init(lim<<1);
  NTT(a,lim,1);
  NTT(b,lim,1);
  LLONG step;
  for(i=0; i<lim; i++){
    step = a[i];
    a[i]=step*b[i]%mod;
  }
  NTT(a,lim,-1);    
  for(i=0,n=0; i<lim; i++)  {
    if(a[i]) n++;
  }
  printf("%d",n);
  return 0;
}

参考文献:

[1]https://tieba.baidu.com/p/8844914020

[2]百度网盘资源下载网址

https://pan.baidu.com/s/17ZXphwqySNIsIgcGtYMjvg?pwd=lhwc

[3]李红卫,李秉璋.?C程序设计与训练(第四版)[M],大连,大连理工大学出版社,2023.

文章来源:https://blog.csdn.net/lhwhit/article/details/135592856
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。