【CodeForces 453B】Little Pony and Harmony Chest

链接:

洛谷

博客园

题目大意:

给定一个数组 (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;
}
原文地址:https://www.cnblogs.com/GJY-JURUO/p/14453716.html