【CodeForces 453B】Little Pony and Harmony Chest

链接:

洛谷

博客园

题目大意:

给定一个数组 \(a\),找到一个两两互质的数组 \(b\) 使得 \(\sum\limits_{i=1}^{n}|a_i-b_i|\) 最小。

正文:

我们可以从 \(a_i\leq 30\) 想到这道题可能与状压沾边。设 \(f_{i,J}\) 表示前 \(i\) 个数选了集合 \(J\) 中的质数作为 \(b_i\) 的质因数。其中集合 \(J\) 在实现中就是那么有:

\[f_{i,J}=\min_{K\subseteq J,k=|J-K|}\{f_{i-1,K}+|a_i-k|\} \]


但是还有一个问题:我们不知道 \(b_i\) 的范围。

这个问题也好解决。还是 \(a_i\leq 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;
}
posted @ 2021-02-26 20:23  Jayun  阅读(60)  评论(0编辑  收藏  举报