LGTB 与序列(数论 (starstar ))
- (LGTB) 有一个长度为 (N) 的序列 (A),现在他想构造一个新的长度为 (N) 的序列 (B),使得 (B) 中的任意两个数都互质。并且他要使[sum_{1le ile N}|A_i-B_i| ]最小,请输出最小值。
Input
- 第一行包含一个数 (N) 代表序列初始长度。
- 接下来一行包含 (N) 个数 (A_1, A_2, …, A_N),代表序列 (A)。
Output
- 输出包含一行,代表最小值。
Sample Input
5
1 6 4 2 8
Sample Output
3
Hint
- 样例解释:(B={1,5,3,2,7}) ,(1) 与任何数都互质。
- 对于 (40\%) 的数据, (1 ≤ N ≤ 10)。
- 对于 (100\%) 的数据, (1 ≤ N ≤ 100, 1 ≤ A_i≤30)。
- 来源:
分析
- (A_ile 30 Rightarrow B_i le 58) ,因为 (B_i) 变成更大的数还不如直接取 (1),而且 (58) 之内只有 (16) 个素数。
- 如果原数列中 (A_i>A_j),那么最优答案一定是 (B_i>B_j) 的情况。所以最多只有 (16) 个最大的数变成素数,其他的数都会变成 (1) 。
- 所以直接对于前 (16) 个数 (dp, dp[i][j]) 代表到第 (i) 个数,哪些素因子被用过了,花费最少为多少。枚举一个数来转移即可
Code
#include <bits/stdc++.h>
const int maxn=105,maxp=17,Inf=0x3f3f3f3f;
int prime[] = {0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53}; // 16 primes
int n,a[maxn];
int dp[maxp][1<<maxp];
int status[maxn];
void Init(){
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",a+i);
std::sort(a+1,a+n+1,std::greater<int>());
memset(status,0,sizeof(status));
for(int i=1;i<=58;i++)
for(int j=1;j<=16;j++)
if(i<prime[j]) break;
else if(i%prime[j]==0)//分解质因数
status[i]|=(1<<j-1);//i的质因数情况
}
void Solve(){
memset(dp,0x3f,sizeof(dp));
dp[0][0]=0;
for(int i=1;i<=std::min(n,16);i++)//最多处理最大的16个
for(int S=0;S<(1<<16);S++) //枚举上一个状态
for(int num=1;num<=58;num++)//枚举每一个可能的B[i]
if(!(status[num]&S)){//状态S中没有num的质因子
int s=S|status[num];//num的质因数都并入下一个状态
dp[i][s]=std::min(dp[i][s],dp[i-1][S]+abs(a[i]-num));
}
int ans=Inf;
for(int S=0;S<(1<<16);S++)//枚举左后一行的所有状态
ans=std::min(ans,dp[std::min(n,16)][S]);
if(n>16)//剩下的均选1
for(int i=17;i<=n;i++)
ans+=abs(a[i]-1);
printf("%d
",ans);
}
int main(){
Init();
Solve();
return 0;
}