专栏文章
题解:P12316 [蓝桥杯 2024 国 C] 循环位运算
P12316题解参与者 2已保存评论 1
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 1 条
- 当前快照
- 1 份
- 快照标识符
- @mipg52od
- 此快照首次捕获于
- 2025/12/03 11:27 3 个月前
- 此快照最后确认于
- 2025/12/03 11:27 3 个月前
题目大意
给定 个数 ,每个数我们都将其视为一个 位的二进制数。你可以进行 次操作,每次选择任意一个数将其循环左移一次。不超过 次操作后, 个数的和最大是多少。
思路
鄙人动态规划不熟,在和机房同学交流后确定是考察动态规划,考虑如何转移。
因为每个数都是 位的二进制数,所以每个数总共只有 种变化,那么问题就转化成:从 到 ,对于每个 取一种变化,使得在 的代价之内和最大。这其实非常像背包 dp。
设 为 循环左移 次得到的数,则状态转移方程如下:
接下来考虑如何求解 数组。因为每次循环左移可以理解为先左移一位,再或上最高位,即
(A << 1) | (A >> 31),所以我们可以在输入时就预处理好 数组。代码
实现的一些小细节都在注释里面,警钟长鸣。
C#include <bits/stdc++.h>
using namespace std;
int n,m;
unsigned int a[1003][33];
long long dp[1003][1003];
int main(){
cin >> n >> m;
for(int i=1;i<=n;i++){
unsigned A; // 注意要非负 防止位运算后成负数 同理 a[i][j] 也要非负
cin >> A;
for(int j=0;j<=31;j++)
{
a[i][j] = A;
A = (A << 1) | (A >> 31);
} // 记得考虑不操作的情况
}
for(int i=1;i<=n;i++)
for(int j=0;j<=m;j++)
for(int k=0;k<=min(31,j);k++) // j-k 不能是负数
dp[i][j] = max(dp[i][j],dp[i-1][j-k]+a[i][k]);
long long ans = -1;
for (int i=0;i<=m;i++)
ans = max(ans,dp[n][i]);
cout << ans;
return 0;
}
相关推荐
评论
共 1 条评论,欢迎与作者交流。
正在加载评论...