链接:
题目大意:
给定一个数组 (a),找到一个两两互质的数组 (b) 使得 (sumlimits_{i=1}^{n}|a_i-b_i|) 最小。
正文:
我们可以从 (a_ileq 30) 想到这道题可能与状压沾边。设 (f_{i,J}) 表示前 (i) 个数选了集合 (J) 中的质数作为 (b_i) 的质因数。其中集合 (J) 在实现中就是那么有:
[f_{i,J}=min_{Ksubseteq J,k=|J-K|}{f_{i-1,K}+|a_i-k|}
]
但是还有一个问题:我们不知道 (b_i) 的范围。
这个问题也好解决。还是 (a_ileq 30),因为 (b_i) 最小是 (1),那么 (|a_i-b_i|) 的上界是 (30-1=29)。也就是说 (b_i) 不超过 (30+29=59),又因为差值如果是 (29) 的话,(b_i=1) 会更优,毕竟 (1) 可以重复选择。所以实际上 (b_i) 不超过 (58)。
以上是我的思考过程,仅供参考。如果要看更严谨的证明,可以移步题解区看 @周子衡 爷的题解。
代码:
我代码里 (b_i) 的上界还是 (59) 的,可以改成 (58)。
int pri[20] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59};
void Output(int x, int y)
{
if (!x) return;
Output(x - 1, y ^ has[ans[x][y]]);
printf ("%d ", ans[x][y]);
}
int main()
{
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
scanf ("%d", &n);
for (int i = 1; i <= n; ++i)
scanf ("%d", &a[i]);
for (int i = 1; i <= 59; i++)
for (int j = 0; j < 17; j++)
if(!(i % pri[j])) has[i] |= 1 << j;
for (int i = 1; i <= n; i++)
{
f[i][0] = 1e9;
for (int j = 0; j < (1 << 17); f[i][++j] = 1e9)
for (int k = 1, tmp = 0; k <= 59; k++)
{
if ((has[k] | j) != j) continue;
tmp = abs(a[i] - k) + f[i - 1][j ^ has[k]];
if (tmp < f[i][j]) f[i][j] = tmp, ans[i][j] = k;
}
}
Output(n, (1 << 17) - 1);
return 0;
}