正在载入在线练习界面,本页内容可直接阅读…

AK CSP › CSP-S 2020 第一轮真题 › 第42题

CSP-S 2020 第一轮 第42题:完善程序(第 20 题)第 4 空

完善程序 · 动态规划 · 答案 A

题目

2.(最优子序列)取 $m = 16$,给出长度为 $n$ 的整数序列 $a_1,a_2,\dots,a_n(0 \le a_i < 2^m)$。对于一个二进制数 $x$,定义其分值 $w(x)$ 为 $x + \operatorname {popcnt}(x)$,其中 $\operatorname{popcnt}(x)$ 表示 $x$ 二进制表示中 $1$ 的个数。对于一个子序列  $b_1,b_2,\dots,b_k$,定义其子序列分值 $S$ 为 $w(b_1 \oplus b_2) + w(b_2 \oplus b_3) + w(b_3 \oplus b_4) + \cdots + w(b_{k-1} \oplus b_k)$。其中 $\oplus$ 表示按位异或。对于空子序列,规定其子序列分值为 $0$ 求一个子序列使得其子序列分值最大,输出这个最大值。

输入第一行包含一个整数 $n(1 \le n \le 40000)$ 接下来一行包含 $n$ 个整数 $a_1,a_2,\cdots,a_n$。

提示:考虑优化朴素的动态规划算法,将前 $\dfrac{m}{2}$ 位和后 $\dfrac{m}{2}$ 位分开计算。

Max[x][y] 表示当前的子序列下一个位置的高 $8$ 位是 $x$、最后一个位置的低 $8$ 位是 $y$ 时的最大价值。

试补全程序。

#include <iostream>

using namespace std;

typedef long long LL;

const int MAXN = 40000, M = 16, B = M >> 1, MS = (1 << B) - 1;
const LL INF = 1000000000000000LL;
LL Max[MS + 4][MS + 4];

int w(int x)
{
    int s = x;
    while(x)
    {
        ①;
        s++;
    }
    return s;
}

void to_max(LL &x, LL y)
{
    if(x < y)
        x = y;
}

int main()
{
    int n;
    LL ans = 0;
    cin >> n;
    for(int x = 0; x <= MS; x++)
        for(int y = 0; y <= MS; y++)
            Max[x][y] = -INF;
    for(int i = 1; i <= n ; i++)
    {
        LL a;
        cin >> a;
        int x = ② , y = a & MS;
        LL v = ③;
        for(int z = 0; z < = MS; z++)
            to_max(v, ④);
        for(int z = 0; z < = MS; z++)
            ⑤;
        to_max(ans , v);
    }
    cout << ans << endl;
    return 0;
}

本小题

④处应填( )

选项

  • A. Max[x][z] + w(y ^ z)
  • B. Max[x][z] + w(a ^ z)
  • C. Max[x][z] + w(x ^ (z << B))
  • D. Max[x][z] + w(x ^ z)

答案

A

题解

应选 A:Max[x][z] + w(y ^ z)。

把当前数 a 拆成高、低各 8 位:

  • x = a >> B:高 8 位;
  • y = a & MS:低 8 位。

假设前一个选中的数,高 8 位为 h,低 8 位为 z,那么连接这两个数产生的分值为:

\[ w(a\oplus b) = w((x\oplus h)\ll B)+w(y\oplus z). \]

能这样拆,是因为高、低位互不重叠,数值和 1 的个数都可以分别相加。

Max[x][z] 已经预先计入了高 8 位的连接分值,也就是维护:

\[ \text{Max}[x][z] =\max_{\text{末尾低8位为 }z} \left(\text{已有子序列分值}+w((x\oplus h)\ll B)\right). \]

所以,当前只需补上低 8 位的贡献 w(y ^ z)。枚举前一个数的低 8 位 z,取最大值:

``cpp to_max(v, Max[x][z] + w(y ^ z)); ``

关键是:高位贡献已存在 Max 中,查询时只加低位贡献。

真题版权归 CCF 所有,本站仅用于非商业教学用途。页面加载后可直接在线作答,作答记录保存在本浏览器或账号中。 京ICP备2026056990号-1 京公网安备11010502062986号