题解:P10911 [蓝桥杯 2024 国 B] 数位翻转
P10911 题解
反转函数的写法
首先按题意把一个十进制数转为二进制,这里我们可以用短除法来解决。
int d[50],now=0;;
while(x>=1){
d[++now]=x%2;
x/=2;
}
然后我们可以使用 reverse() 函数来反转这个 d 数组。
reverse(d+1,d+now+1);
这个函数的使用方法就像 sort() 一样,此处不再赘述。
最后把这个二进制数再转回十进制。
int sum=0;
for(int i=1;i<=now;i++){
if(d[i]) sum+=mem[i-1];
}
思路分析
我的第一个想法就时贪心,但又不能找到一个具有普遍性的贪心策略,于是想到了动态规划。
dp 数组的定义
先来看一下下面这张图片。
因为题目中提到了区间,所以我们先把这些区间画下来。
图中带有
观察发现,偶数个的区间都要经过反转。
因为经过反转的区间的两旁肯定是没有经过反转的区间。
当然,区间的长度可能为 0。
所以我们定义
状态转移方程与目标
可推出:
解释:
对于
如果不能理解,可以再仔细想想
可推出答案
解释:
最多有
代码
#include<iostream>
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=3005;
int a[N],dp[N][N],rev[N];
int f(int x){//反转函数
int d[50],now=0;
while(x>=1){
d[++now]=x%2;
x>>=1;
}
reverse(d+1,d+now+1);
int sum=0;
for(int i=1;i<=now;i++) if(d[i]) sum+=1<<i-1;
return sum;
}
signed main(){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) rev[i]=f(a[i]);
for(int i=1;i<=n;i++){
for(int j=1;j<=2*m+1;j++){
int flag=a[i];
if(j%2==0) flag=rev[i];
dp[i][j]=max(dp[i-1][j],dp[i-1][j-1])+flag;//核心
}
}
int ans=-1;
for(int i=1;i<=2*m+1;i++){
ans=max(ans,dp[n][i]);
}
cout<<ans;
}
都写的这么全了,给个赞再走吧 QwQ。