洛谷 P12316 [蓝桥杯 2024 国 C] 循环位运算 题解
题目大意
给你 \(n\) 个数,总共可对这些数进行 \(m\) 次循环位运算,求在操作后 \(n\) 个数最大的和。所有数看成 \(32\) 位二进制数。
思路
看到题目就能想到是动态规划。
定义 \(dp_{i,j}\) 为前 \(i\) 个数总共操作 \(j\) 次的前 \(i\) 个数的和的最大值(好绕口)。
同时,我们为节省时间,提前预处理一个二维数组 \(num_{i,j}\) 表示第 \(i\) 个数进行 \(j\) 次操作的结果。
那么,可以推出状态转移方程为 \(dp_{i,j}=\max (dp_{i,j},dp_{i-1,j-k}+num_{i,k})\)。
代码实现
第一部分:预处理
我们对于某一次操作,可以将待操作的数先左移一位,再取出第 \(32\) 位上的零一值,将该位变为零,并将该值移到第 \(1\) 位上。
代码片段如下:
	for(int i=1;i<=n;++i)
	{
		cin>>num[i][0];
		for(int j=1;j<=m;++j)
		{
			num[i][j]=(num[i][j-1]<<1ll);
			num[i][j]+=((num[i][j]&(1ll<<32))>>32);
			num[i][j]-=(num[i][j]&(1ll<<32));
		}
	}
第二部分:动态规划
考虑三重循环,最外层循环从 \(1\) 到 \(n\) 枚举数 \(i\),第二层从 \(0\) 到 \(m\) 枚举前 \(i\) 个数的总共操作 \(j\) 次,最后一层循环从 \(0\) 到 \(j\) 枚举第 \(i\) 个数的操作 \(k\) 次。
最后的答案为最大的 \(dp_{n,i}\),\(i\) 从 \(0\) 到 \(m\)。
代码片段如下:
	for(int i=1;i<=n;++i)
	{
		for(int j=0;j<=m;++j)
		{
			for(int k=0;k<=j;++k)
			{
				dp[i][j]=max(dp[i][j],dp[i-1][j-k]+num[i][k]);
			}
		}
	}
	for(int i=0;i<=m;++i)
	{
		ans=max(ans,dp[n][i]);
	}
	cout<<ans;
第三部分:代码整体实现
直接上代码:
#include<bits/stdc++.h>
#define ll int
using namespace std;
ll dp[1005][1005],num[1005][1005],ans,n,m;
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;++i)
	{
		cin>>num[i][0];
		for(int j=1;j<=m;++j)
		{
			num[i][j]=(num[i][j-1]<<1ll);
			num[i][j]+=((num[i][j]&(1ll<<32))>>32);
			num[i][j]-=(num[i][j]&(1ll<<32));
		}
	}
	for(int i=1;i<=n;++i)
	{
		for(int j=0;j<=m;++j)
		{
			for(int k=0;k<=j;++k)
			{
				dp[i][j]=max(dp[i][j],dp[i-1][j-k]+num[i][k]);
			}
		}
	}
	for(int i=0;i<=m;++i)
	{
		ans=max(ans,dp[n][i]);
	}
	cout<<ans;
	return 0;
}
警钟长鸣
由于本人的预处理做法先进行左移,导致使用 int 会出错,因此使用 long long 型变量。
如有问题请直接提出,我会改进该篇题解。
最后感谢您的留步与观看,希望本篇题解能够帮到您。

                
            
        
浙公网安备 33010602011771号