洛谷 P1414 又是毕业季II(最大公约数)

题目链接

https://www.luogu.org/problemnew/show/P1414

题目背景

“叮铃铃铃”,随着高考最后一科结考铃声的敲响,三年青春时光顿时凝固于此刻。毕业的欣喜怎敌那离别的不舍,憧憬着未来仍毋忘逝去的歌。1000多个日夜的欢笑和泪水,全凝聚在毕业晚会上,相信,这一定是一生最难忘的时刻!

题目描述

彩排了一次,老师不太满意。当然啦,取每位同学的号数来找最大公约数显然不太合理。于是老师给每位同学评了一个能力值。于是现在问题变为,从n个学生中挑出k个人使得他们的默契程度(即能力值的最大公约数)最大。但因为节目太多了,而且每个节目需要的人数又不知道。老师想要知道所有情况下能达到的最大默契程度是多少。这下子更麻烦了,还是交给你吧~

PS:一个数的最大公约数即本身。

输入输出格式

输入格式:

第一行一个正整数n。

第二行为n个空格隔开的正整数,表示每个学生的能力值。

输出格式:

总共n行,第i行为k=i情况下的最大默契程度。

输入输出样例

输入样例#1:
4
1 2 3 4
输出样例#1:
4
2
1
1

说明

【数据范围】

记输入数据中能力值的最大值为inf。

对于20%的数据,n<=5,inf<=1000

对于另30%的数据,n<=100,inf<=10

对于100%的数据,n<=10000,inf<=1e6

解题思路

这个题就是求从n个数中选1...n个数时的最大公因数。

首先,数据范围较大,暴力枚举肯定要炸,所以我们需要换一种思路。

我们用vis[i]存下i出现的次数,用num[i]表示以i为因数的数(i的倍数)一共有多少个,用ans[i]表示选i个数的最大公因数。 

所以我们枚举每一个因数,记录下这一个因数在这n个数中有多少个倍数,用num记录。

然后枚举每一个因数,根据num得知他是多少个数的因数,然后不断更新答案。

因为枚举因数时是从小到大枚举的,所以ans的每一次更新的值都会比原来的数值大,保证了答案的正确性。

具体过程请看代码(内有具体思路)。

 
 1 #include<iostream>
 2 #include<cmath>
 3 using namespace std;                        //vis[i]表示 i出现的次数
 4 int n,vis[1000005],num[1000005],ans[10005]; //num[i]表示以i为因数的数(i的倍数)一共有多少个,ans[i]表示选i个数的最大公因数  
 5 int maxx;                                     //maxx存的是n个正整数的最大值。这样节约了时间 
 6 int main()
 7 {
 8 cin>>n;
 9 for(int i=1;i<=n;i++){
10     int in;
11     cin>>in;
12     vis[in]++;
13     maxx=max(in,maxx);                        //更新最大值 
14 }
15 for(int i=1;i<=maxx;i++)                    //i枚举的是因数 
16 for(int j=i;j<=maxx;j+=i){                    //j枚举的是i的倍数 
17     num[i]+=vis[j];                            //i的倍数加上j这个数出现的次数 
18 }
19 for(int i=1;i<=maxx;i++)                    //i枚举的是因数 
20 for(int j=1;j<=num[i];j++){                    //j枚举的是这个因数的倍数的个数 
21     ans[j]=i;                                //选j个数的最大公因数有可能是i(但不一定是,至少到目前为止是) 
22 }
23 for(int i=1;i<=n;i++) cout<<ans[i]<<endl;    //输出 
24 return 0;
25 }
AC代码(附解析)
原文地址:https://www.cnblogs.com/yinyuqin/p/10474023.html