蓝桥杯2024年第十五届决赛真题-数位翻转

https://www.dotcpp.com/oj/problem3293.html?sid=16920906&lang=1#editor

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

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

输入格式
输入共 2 行。
第一行为两个正整数 n, m。
第二行为 n 个由空格分开的整数 a1, a2, ..., an。

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

样例输入
5 3
11 12 13 14 15
样例输出
67
提示
【样例说明 1】只翻转 a1,和为 13 + 12 + 13 + 14 + 15 = 67。

样例输入
6 2
23 8 11 19 16 35
样例输出
134
翻转区间 [a3, a4] 和 [a6],和为 23 + 8 + 13 + 25 + 16 + 49 = 134。

【评测用例规模与约定】
对于 20% 的评测用例,保证 n, m ≤ 20。
对于 100% 的评测用例,保证 n, m ≤ 1000,\(0 ≤ a_i ≤ 10^9\)

这个题是个动态规划问题,设置 dp 数组\(dp[i][j][0/1]\)。其中\(dp[i][j][0]\)表示第 \(i\) 位不翻转时,恰好前 \(i\) 位共有 \(j\) 位翻转的最优情况;响应的\(dp[i][j][1]\)表示翻转。
状态转移方程就是:

\[dp[i][j][0] = \begin{split} \begin{cases} dp[i-1][j][0]+a[i],\\ dp[i-1][j][1]+a[i], \\ \end{cases}\end{split} \]

\[dp[i][j][1] = \begin{split} \begin{cases} dp[i-1][j-1][0]+b[i],\\ dp[i-1][j][1]+b[i], \\ \end{cases}\end{split} \]

#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e3+100;
ll a[maxn];
ll b[maxn];
ll tmp[100000];
ll dp[maxn][maxn][2];//1是这个变化,0是不变化 
ll get(ll x){
	
	ll ans=0,p=1,cnt=0;
	while(x){
		tmp[++cnt]=x%2;
		x/=2;
	}
	for(int i=cnt;i>=1;i--){
		ans=ans+p*(tmp[i]);
		p=p*2ll;
	}
	return ans;
}
int main(){
	int n,m;
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
		b[i]=get(a[i]);
	}
	for(int i=1;i<=n;i++){
		dp[i][0][0]=dp[i-1][0][0]+a[i];
		for(int j=1;j<=m;j++){
			dp[i][j][0]=max(dp[i-1][j][0],dp[i-1][j][1])+a[i];
			dp[i][j][1]=max(dp[i-1][j-1][0]+b[i],dp[i-1][j][1]+b[i]);
		}
	}
	ll ans=0;
	for(int j=0;j<=m;j++){
		ans=max(ans,max(dp[n][j][0],dp[n][j][1]));
	}
	cout<<ans<<endl;
}
posted @ 2024-06-08 15:52  lipu123  阅读(340)  评论(0)    收藏  举报