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);
}

posted @ 2022-03-05 23:07  Bellala  阅读(38)  评论(0)    收藏  举报