海亮OJ庆生赛题解(福星)
刘老师生日=庆生赛???这次比赛的题目都比较虚伪。没有稳拿的水题QAQ所以就要用贪心算法----挑简单的做。
首先是第三题《福星》,简单说一下题目描述:
每个人都有一个号码,如果第 i个人的幸运号码能被第 j个人的幸运号码整除, 那么第 j 个人就是第 i个人的福星。自己当然不能是自己的福星。
求每个人有多少个福星。。。
思路:题目意思就是求每个数能被多少个数整除,显然用暴力是肯定行不通的,应为数据实在是太大了。所以就要想到如何进行优化,减少时间复杂度。然后首先我就立马想到了素数筛法(详细代码见黑皮书P174页),素数筛法是把每个素数的被数都筛一遍。这题思路其实也可以想成刚好相反的算法,把合数“筛”一遍(筛法当然不同)。暂且把这种思路称为“反向筛”。然后用这种思路把每个数筛一遍,最后输出,但是也还要考虑很多细节(不然也不会就这么点分了QAQ)。这种反向筛具体思路见代码。
#include<bits/stdc++.h>
using namespace std;
int n,m,i,j,a[1000001]={},b[1000001]={},c[1000001]={};
int main()
{
cin>>n;
for(i=1;i<=n;i++)
{
cin>>a[i]; b[a[i]]++;
if(a[i]>m) m=a[i];
}
for(i=1;i<=m;i++)
if(b[i]>0) for(j=i;j<=m;j+=i) c[j]+=b[i];
for(i=1;i<=n;i++) cout<<c[a[i]]-1<<endl;
return 0;
}
这样”反向筛“的算法应该是没有问题了,但提交上去显然会发现只拿到了部分分,剩下的最后几个点全部是超时!?
现在就要处理具体细节了,既然是超时,就一定要调整部分数组,然后因为数据太大,所以要改成scanf和printf。
AC代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,m=0,i,j,a[100010]={},b[1000100]={},c[1000100]={};
int main()
{
cin>>n;
for(i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[a[i]]++;
if(a[i]>m) m=a[i];
}
for(i=1;i<=m;i++)
if(b[i]>0) for(j=i;j<=m;j+=i) c[j]+=b[i];
for(i=1;i<=n;i++) printf("%d\n",c[a[i]]-1);
return 0;
}
但程序还可以有一定优化,这是别人的优化代码:
#include <bits/stdc++.h>
#pragma GCC optimize(2)
using namespace std;
int a[1000010]={},b[1000010]={},c[1000010]={};
int main()
{
int n,m;
cin>>n;
int Max=-1;
for (int i=1;i<=n;i++)
{
scanf("%d",&a[i]);
b[a[i]]++;
if (a[i]>Max) Max=a[i];
}
for(int i=1;i<=Max;i++)
{
if (b[i]>0)
for(int j=i;j<=Max;j=j+i)
c[j]=c[j]+b[i];
}
for(int i=1;i<=n;i++)
printf("%d\n",c[a[i]]-1);
return 0;
}
看完了别人的代码,就知道自己还有很多不足,还要多刷题练手速QAQ