题解 CF1183F 【Topforces Strikes Back】

wucstdio

2019-06-27 17:08:02

Solution

## 题意 从$n$个数中选出来最多$3$个数,使得这三个数中不存在任意一个数是另一个数的倍数,同时使得这三个数的和最大。 ## 题解 这是一道十分巧妙的贪心题。 首先我们将答案按照选出来的数字个数分情况。 ### 一 如果选出来的数字个数为$1$,显然答案就是最大的数字。 ### 二 如果选出来的数字个数为$2$,我们考虑最大的数$a$是否在最优答案里。 答案是肯定的,因为如果选出来的两个数$x,y$都不是$a$,那么这样的答案一定不会更大。证明如下: 1、如果有一个数不是$a$的因数,那么直接用$a$替换这个数。 2、如果两个数都是$a$的因数,那么这两个数的和最大是$\dfrac a2+\dfrac a3<a$,也就是说我们在步骤一中就已经得到最优解了。 所以对于这一种情况,我们只需要选出来最大的数,然后从剩下的数中找到一个不是它的因数的最大的数即可。 ### 三 如果选出来的数字个数为$3$,我们还是考虑最大的数$a$是否在最优答案里。 可以发现,如果选出来的三个数中有一个或两个数不是$a$的因数,那么就可以由情况二进行证明,一定要选出来$a$。 而如果三个数都是$a$的因数,我们发现有唯一的一种特殊情况:$\dfrac a2+\dfrac a3+\dfrac a5>a$,其余的都小于$a$。 所以我们需要特判一下$\dfrac a2+\dfrac a3+\dfrac a5$这种情况,特判完后就选出来最大的$a$并把它所有的因数删掉,然后用情况二中的方法来做。 时间复杂度$O(n)$。 ```cpp #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int t,n,a[200005]; int main() { scanf("%d",&t); while(t--) { scanf("%d",&n); int ans=0; for(int i=1;i<=n;i++) { scanf("%d",&a[i]); ans=max(ans,a[i]); } sort(a+1,a+n+1); int an=a[n]; bool flag1=0,flag2=0,flag3=0; for(int i=1;i<=n;i++) { if(a[i]*2==an)flag1=1; if(a[i]*3==an)flag2=1; if(a[i]*5==an)flag3=1; } if(flag1&&flag2&&flag3)ans=max(ans,an/2+an/3+an/5); for(int i=1;i<=n;i++) if(a[n]%a[i]==0)a[i]=2000000000; sort(a+1,a+n+1); while(a[n]==2000000000)n--; ans=max(ans,an+a[n]); for(int i=1;i<=n;i++) if(a[n]%a[i]!=0)ans=max(ans,an+a[n]+a[i]); printf("%d\n",ans); } return 0; } ```