hi.数位翻转

P10911 [蓝桥杯 2024 国 B] 数位翻转

题目描述

小明创造了一个函数 f ( x ) f(x) f(x) 用来翻转 x x x 的二进制的数位(无前导 0 0 0)。比如 f ( 11 ) = 13 f (11) = 13 f(11)=13,因为 11 = ( 1011 ) 2 11 = (1011)_2 11=(1011)2,将其左右翻转后,变为 13 = ( 1101 ) 2 13 = (1101)_2 13=(1101)2;再比如 f ( 3 ) = 3 f (3) = 3 f(3)=3 f ( 0 ) = 0 f (0) = 0 f(0)=0 f ( 2 ) = f ( 4 ) = f ( 8 ) = 1 f (2) = f (4) = f (8) = 1 f(2)=f(4)=f(8)=1 等等。

小明随机出了一个长度为 n n n 的整数数组 { a 1 , a 2 , ⋯   , a n } \{a_1, a_2,\cdots, a_n\} {a1,a2,,an},他想知道,在这个数组中选择最多 m m m 个不相交的区间,将这些区间内的数进行二进制数位翻转(将
a i a_i ai 变为 f ( a i ) f (a_i) f(ai))后,整个数组的和最大是多少?

输入格式

输入共 2 2 2 行。

第一行为两个正整数 n , m n,m n,m

第二行为 n n n 个由空格分开的整数 a 1 , a 2 , ⋯   , a n a_1, a_2, \cdots, a_n a1,a2,,an

输出格式

输出共 1 1 1 行,一个整数表示答案。

输入输出样例 #1

输入 #1

5 3
11 12 13 14 15

输出 #1

67

输入输出样例 #2

输入 #2

6 2
23 8 11 19 16 35

输出 #2

134

说明/提示

hi.数位翻转_第1张图片
hi.数位翻转_第2张图片

【样例说明 1】

只翻转 a 1 a_1 a1,和为 13 + 12 + 13 + 14 + 15 = 67 13 + 12 + 13 + 14 + 15 = 67 13+12+13+14+15=67

【样例说明 2】

翻转区间 [ a 3 , a 4 ] [a_3, a_4] [a3,a4] [ a 6 ] [a_6] [a6],和为 23 + 8 + 13 + 25 + 16 + 49 = 134 23 + 8 + 13 + 25 + 16 + 49 = 134 23+8+13+25+16+49=134

【评测用例规模与约定】

对于 20 % 20\% 20% 的评测用例,保证 n , m ≤ 20 n,m \le 20 n,m20
对于 100 % 100\% 100% 的评测用例,保证 n , m ≤ 1000 n,m \le 1000 n,m1000 0 ≤ a i ≤ 10 9 0 \le a_i \le 10^9 0ai109

代码内容

#include 
using namespace std;

typedef long long ll;
const ll N=3e3+10;
ll a[N],dp[N][N],rev[N];

ll f(ll x)//反转函数
{
    ll d[50],cnt=0;
    while(x)
    {
        d[++cnt]=x%2;
        x>>=1;
    }
    
    reverse(d+1,d+cnt+1);
    
    ll sum=0;
    for(ll i=1;i<=cnt;i++)  if(d[i]) sum+=1<<(i-1);
    return sum;
}

int main()
{
    ll n,m;
    cin>>n>>m;
    
    for(ll i=1;i<=n;i++) cin>>a[i];
    for(ll i=1;i<=n;i++) rev[i]=f(a[i]);
    for(ll i=1;i<=n;i++)
        for(ll j=1;j<=2*m+1;j++)
        {
            ll flag=a[i];
            if(j%2==0) flag=rev[i];
            dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+flag;//核心
		}

	ll ans=-1;
	for(ll i=1;i<=2*m+1;i++) ans=max(ans,dp[n][i]);

	cout<<ans<<endl;
    return 0;
}

你可能感兴趣的:(c++,算法)