LGTB 与序列

LGTB 与序列(数论 \(\star\star \))

  • \(LGTB\) 有一个长度为 \(N\) 的序列 \(A\),现在他想构造一个新的长度为 \(N\) 的序列 \(B\),使得 \(B\) 中的任意两个数都互质。并且他要使

    \[\sum_{1\le i\le 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_i\le 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\n",ans);
}
int main(){    
    Init();
    Solve();    
    return 0;
}
posted @ 2020-07-06 11:41  ♞老姚♘  阅读(763)  评论(0编辑  收藏  举报