欢迎光临
我们一直在努力

2020年信奥赛C++提高组csp-s初赛真题及答案解析(完善程序第1题)

2020年信奥赛C++提高组csp-s初赛真题及答案解析(完善程序第1题)

在这里插入图片描述

第2题

(最优子序列)取 m=16,给出长度为 n的整数序列

a

1

,

a

2

,

,

a

n

(

0

a

i

<

2

m

)

a_1,a_2,…,a_n(0≤a_i<2^m)

a1,a2,,an(0ai<2m)。对于一个二进制数 x,定义其分值 w(x)为 x+popcnt⁡(x),其中 popcnt⁡(x) 表示 x二进制表示中 1的个数。对于一个子序列

b

1

,

b

2

,

,

b

k

b_1,b_2,…,b_k

b1,b2,,bk,定义其子序列分值 S为

w

(

b

1

b

2

)

+

w

(

b

2

b

3

)

+

w

(

b

3

b

4

)

+

+

w

(

b

k

1

b

k

)

w(b_1⊕b_2)+w(b_2⊕b_3)+w(b_3⊕b_4)+⋯+w(b_{k−1}⊕b_k)

w(b1b2)+w(b2b3)+w(b3b4)++w(bk1bk)。其中 ⊕表示按位异或。对于空子序列,规定其子序列分值为 0求一个子序列使得其子序列分值最大,输出这个最大值。

输入第一行包含一个整数 n(1≤n≤40000)接下来一行包含 nn 个整数

a

1

,

a

2

,

,

a

n

a_1,a_2,…,a_n

a1,a2,,an

提示:考虑优化朴素的动态规划算法,将前

m

2

\\frac{m}{2}

2m位和后

m

2

\\frac{m}{2}

2m位分开计算。

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. x >>= 1

    B. x ^= x &(x ^ (x + 1))

    C. x -= x | -x

    D. x ^= x &(x ^ (x – 1))

  • ②处应填( )

    A. (a & MS) << B

    B. a >> B

    C. a & (1 << B)

    D. a & (MS << B)

  • ③处应填( )

    A. -INF

    B. Max[y][x]

    C. 0

    D. Max[x][y]

  • ④处应填( )

    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. to_max(Max[y][z], v + w(a ^ (z << B)))

    B. to_max(Max[z][y], v + w((x ^ z) << B))

    C. to_max(Max[z][y], v + w(a ^ (z << B)))

    D. to_max(Max[x][z], v + w(y ^ z))

  • 题目分析

    本题要求计算一个子序列的最大分值,其中分值为相邻元素异或值的 (w(x)=x+\\operatorname{popcnt}(x)) 之和。直接枚举子序列的复杂度太高,需要利用动态规划并优化转移。

    关键性质

    将每个数 (a) 拆分为高8位 (h) 和低8位 (l)(因为 (m=16),(B=8))。对于两个数 (a=(h_1,l_1)) 和 (b=(h_2,l_2)),有: $ a \\oplus b = (h_1 \\oplus h_2) \\ll 8 ;\\big|; (l_1 \\oplus l_2) $ 由于高低位不重叠,

    w

    (

    a

    b

    )

    w(a \\oplus b)

    w(ab) 可以分解为: $ w(a \\oplus b) = w\\big((h_1 \\oplus h_2) \\ll 8\\big) + w(l_1 \\oplus l_2) $ 其中

    w

    (

    x

    8

    )

    =

    (

    x

    8

    )

    +

    popcnt

    (

    x

    )

    w(x \\ll 8) = (x \\ll 8) + \\operatorname{popcnt}(x)

    w(x8)=(x8)+popcnt(x),而

    w

    (

    l

    1

    l

    2

    )

    w(l_1 \\oplus l_2)

    w(l1l2) 是8位数的分值。

    动态规划状态设计

    设 dp[i]表示以第 i个数结尾的子序列的最大分值。转移时需枚举前一个数 j: $ dp[i] = \\max\\left(0,\\ \\max_{j < i} \\big( dp[j] + w((h_i \\oplus h_j) \\ll 8) + w(l_i \\oplus l_j) \\big) \\right) $ 直接枚举 j 是

    O

    (

    n

    2

    )

    O(n^2)

    O(n2) 不可行。利用分解式,我们可以将状态按高8位分组,并维护一个二维数组

    M

    a

    x

    [

    u

    ]

    [

    v

    ]

    Max[u][v]

    Max[u][v],其含义为:

    • 对于所有之前已经处理过的数,固定未来数的高8位为 (u),且当前数的低8位为 (v) 时,所能获得的最大“部分价值”,即: $ Max[u][v] = \\max_{\\text{之前的数 } b=(h_b,l_b)} \\big( dp[b] + w((u \\oplus h_b) \\ll 8) \\big) $ 这里 v 对应之前数的低8位

      l

      b

      l_b

      lb

    那么对于当前数

    a

    =

    (

    h

    ,

    l

    )

    a=(h,l)

    a=(h,l),考虑从任意之前的数 b 转移:

    • 从 b 转移的价值为

      d

      p

      [

      b

      ]

      +

      w

      (

      (

      h

      h

      b

      )

      8

      )

      +

      w

      (

      l

      l

      b

      )

      dp[b] + w((h \\oplus h_b) \\ll 8) + w(l \\oplus l_b)

      dp[b]+w((hhb)8)+w(llb)

    • 若固定 b 的低8位

      l

      b

      =

      v

      l_b = v

      lb=v,则

      M

      a

      x

      [

      h

      ]

      [

      v

      ]

      Max[h][v]

      Max[h][v] 已经存储了所有满足

      l

      b

      =

      v

      l_b=v

      lb=v 的 b中

      d

      p

      [

      b

      ]

      +

      w

      (

      (

      h

      h

      b

      )

      8

      )

      dp[b] + w((h \\oplus h_b) \\ll 8)

      dp[b]+w((hhb)8)的最大值。

    • 因此,对于当前 a,只需枚举所有可能的 v,取

      max

      v

      (

      M

      a

      x

      [

      h

      ]

      [

      v

      ]

      +

      w

      (

      l

      v

      )

      )

      \\max_v (Max[h][v] + w(l \\oplus v))

      maxv(Max[h][v]+w(lv)),再与0比较即得 dp[a]。

    状态更新

    计算出 dp[a] 后,需要用 a更新 Max数组,以便后续数使用。对于任意未来的高8位 u,a 贡献的价值为

    d

    p

    [

    a

    ]

    +

    w

    (

    (

    u

    h

    )

    8

    )

    dp[a] + w((u \\oplus h) \\ll 8)

    dp[a]+w((uh)8),且其低8位为 l。因此更新: $ Max[u][l] = \\max\\big(Max[u][l],\\ dp[a] + w((u \\oplus h) \\ll 8)\\big) $

    算法流程
  • 初始化 (Max) 为

    -\\infty

    (负无穷)。

  • 依次读入每个数 a:
    • 计算高8位

      x

      =

      a

      B

      x = a \\gg B

      x=aB,低8位

      y

      =

      a

      &

      M

      S

      y = a \\& MS

      y=a&MS

      M

      S

      =

      (

      1

      <

      <

      B

      )

      1

      MS = (1<<B)-1

      MS=(1<<B)1)。

    • 设当前最优值 v = 0(空子序列)。
    • 枚举所有

      z

      =

      0

      M

      S

      z = 0 \\ldots MS

      z=0MS,用

      M

      a

      x

      [

      x

      ]

      [

      z

      ]

      +

      w

      (

      y

      z

      )

      Max[x][z] + w(y \\oplus z)

      Max[x][z]+w(yz) 更新 (v)(取最大值)。

    • 记录答案

      a

      n

      s

      =

      max

      (

      a

      n

      s

      ,

      v

      )

      ans = \\max(ans, v)

      ans=max(ans,v)

    • 枚举所有

      z

      =

      0

      M

      S

      z = 0 \\ldots MS

      z=0MS,更新

      M

      a

      x

      [

      z

      ]

      [

      y

      ]

      =

      max

      (

      M

      a

      x

      [

      z

      ]

      [

      y

      ]

      ,

       

      v

      +

      w

      (

      (

      x

      z

      )

      B

      )

      )

      Max[z][y] = \\max(Max[z][y],\\ v + w((x \\oplus z) \\ll B))

      Max[z][y]=max(Max[z][y], v+w((xz)B))

  • 输出 (ans)。
  • 时间复杂度:每个数处理两个 256的循环,共

    O

    (

    n

    ×

    512

    )

    O(n \\times 512)

    O(n×512),空间

    O

    (

    256

    2

    )

    O(256^2)

    O(2562),可行。

    答案及解析
    • ① 计算 (w(x)) 需要统计 (x) 的二进制1个数。常用技巧是每次消去最低位的1:x ^= x & (x ^ (x-1)) 等价于 x &= x-1,但这里用异或实现。选 D 。
    • ② 取高8位:a >> B,B=8,选 B。
    • ③ 初始化 v 为 0,表示空子序列,选 C。
    • ④ 转移时用

      M

      a

      x

      [

      x

      ]

      [

      z

      ]

      +

      w

      (

      y

      z

      )

      Max[x][z] + w(y \\oplus z)

      Max[x][z]+w(yz),选 A。

    • ⑤ 更新时用

      M

      a

      x

      [

      z

      ]

      [

      y

      ]

      =

      max

      (

      ,

      v

      +

      w

      (

      (

      x

      z

      )

      B

      )

      )

      Max[z][y] = \\max(\\dots, v + w((x \\oplus z) \\ll B))

      Max[z][y]=max(,v+w((xz)B)),选 B。


    专栏推荐:信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新) https://blog.csdn.net/weixin_66461496/category_13125089.html


    各种学习资料,助力大家一站式学习和提升!!!

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
    cout<<"########## 一站式掌握信奥赛知识! ##########";
    cout<<"############# 冲刺信奥赛拿奖! #############";
    cout<<"###### 课程购买后永久学习,不受限制! ######";
    return 0;
    }

    1、csp信奥赛高频考点知识详解及案例实践:

    CSP信奥赛C++动态规划: https://blog.csdn.net/weixin_66461496/category_13096895.html点击跳转

    CSP信奥赛C++标准模板库STL: https://blog.csdn.net/weixin_66461496/category_13108077.html 点击跳转

    信奥赛C++提高组csp-s知识详解及案例实践: https://blog.csdn.net/weixin_66461496/category_13113932.html

    2、csp信奥赛冲刺一等奖有效刷题题解:

    CSP信奥赛C++初赛及复赛高频考点真题解析(持续更新):https://blog.csdn.net/weixin_66461496/category_12808781.html 点击跳转

    CSP信奥赛C++一等奖通关刷题题单及题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12673810.html 点击跳转

    信奥赛C++提高组csp-s初赛&复赛真题题解(持续更新) https://blog.csdn.net/weixin_66461496/category_13125089.html

    3、GESP C++考级真题题解:

    在这里插入图片描述

    GESP(C++ 一级+二级+三级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12858102.html 点击跳转

    在这里插入图片描述

    GESP(C++ 四级+五级+六级)真题题解(持续更新):https://blog.csdn.net/weixin_66461496/category_12869848.html 点击跳转

    在这里插入图片描述 GESP(C++ 七级+八级)真题题解(持续更新): https://blog.csdn.net/weixin_66461496/category_13117178.html

    4、CSP信奥赛C++竞赛拿奖视频课:

    https://edu.csdn.net/course/detail/40437 点击跳转 在这里插入图片描述

    · 文末祝福 ·

    #include<bits/stdc++.h>
    using namespace std;
    int main(){
    cout<<"跟着王老师一起学习信奥赛C++";
    cout<<" 成就更好的自己! ";
    cout<<" csp信奥赛一等奖属于你! ";
    return 0;
    }

    赞(0)
    未经允许不得转载:171主机测评 » 2020年信奥赛C++提高组csp-s初赛真题及答案解析(完善程序第1题)
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址