cf453 B. Little Pony and Harmony Chest(状压dp+回溯、数论)
题意:
给定长为 n 的数组 \(a[]\),要构造长为 n 的正数数组 \(b[]\),要求 \(b[]\) 中的数两两互质,且最小化 \(val=\sum\limits _{i=1}^n |a_i-b_i|\) 。输出 \(b[]\) 。
\(1\le n \le 100, 1\le a_i\le 30\)
思路:
若存在某 \(b_i\ge 59\),那就不如把 \(b_i\) 改成1,因为 \(b_i-a_i\ge a_i-1\),且改成1不影响互质。
所以 \(1\le b_i< 59\)。所有 \(b_i\) 的质因子分解式中,出现的质数只可能为 \(2,3,5,7,\cdots,53\),共16个。
用状态 s 记录选了哪些质数。\(f(i,s)\) 表示考虑 \(a[1\sim i]\),已经用过的质数集为 s 的最小 val 值。
枚举上一个的状态 s,再从1到58枚举现在要用哪个数,更新状态。记录s用于回溯。我这里回溯数组就直接存答案了。
const int N = 103, M = (1<<16); //状态为 0 ~ (1<<16)-1
int n, a[N], f[N][M], ans[N][M];
const int p[16] = {2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53};
int have[60]; //i有哪些质因子
void gethave()
{
for(int x = 1; x <= 58; x++)
for(int i = 0; i < 16; i++)
if(x % p[i] == 0) have[x] |= (1<<i);
}
void pri(int i, int s)
{
if(!i) return;
pri(i - 1, s ^ have[ans[i][s]]); //去掉现在的状态
cout << ans[i][s] << ' ';
}
main()
{
iofast;
gethave();
cin >> n; for(int i = 1; i <= n; i++) cin >> a[i];
memset(f, INF, sizeof f);
f[0][0] = 0;
for(int i = 1; i <= n; i++)
for(int s = 0; s < M; s++)
for(int b = 1; b <= 58; b++)
if((have[b] & s) == 0) //没交集
{
int val = f[i-1][s] + abs(a[i] - b);
if(f[i][s|have[b]] > val)
f[i][s|have[b]] = val,
ans[i][s|have[b]] = b;
}
int minS = min_element(f[n], f[n]+M) - f[n];
pri(n, minS);
}

浙公网安备 33010602011771号