专栏文章

题解:P12316 [蓝桥杯 2024 国 C] 循环位运算

P12316题解参与者 2已保存评论 1

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
1 条
当前快照
1 份
快照标识符
@mipg52od
此快照首次捕获于
2025/12/03 11:27
3 个月前
此快照最后确认于
2025/12/03 11:27
3 个月前
查看原文

题目大意

给定 nn 个数 AiA_i,每个数我们都将其视为一个 3232 位的二进制数。你可以进行 mm 次操作,每次选择任意一个数将其循环左移一次。不超过 mm 次操作后,nn 个数的和最大是多少。

思路

鄙人动态规划不熟,在和机房同学交流后确定是考察动态规划,考虑如何转移。
因为每个数都是 3232 位的二进制数,所以每个数总共只有 3232 种变化,那么问题就转化成:从 11nn,对于每个 AiA_i 取一种变化,使得在 mm 的代价之内和最大。这其实非常像背包 dp。
ai,ja_{i,j}AiA_i 循环左移 jj 次得到的数,则状态转移方程如下:
dpi,j=max(dpi,j,dpi1,jk+ai,k)dp_{i,j} = \max(dp_{i,j},dp_{i-1,j-k}+a_{i,k})
接下来考虑如何求解 ai,ja_{i,j} 数组。因为每次循环左移可以理解为先左移一位,再上最高位,即 (A << 1) | (A >> 31),所以我们可以在输入时就预处理好 ai,ja_{i,j} 数组。

代码

实现的一些小细节都在注释里面,警钟长鸣。
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 条评论,欢迎与作者交流。

正在加载评论...