(一)小红的数组操作

链接:ac.nowcoder.com/acm/contest...

来源:牛客网

一、分步拆解完整解题思路

步骤 1:分桶 ------ 按二进制位归类下标

核心操作代码(22~26 行)

ini 复制代码
for(int i=1;i<=n;i++){
    bitset<32> bt(a[i]);
    for(int j=0;j<=30;j++){
        if(bt[j]) v[j].push_back(i);
    }
}

逻辑:一个数字二进制有多位 1,它的下标会被放进对应所有位的桶里 。例:a[3]=5 二进制 101,第 0、2 位是 1 → v[0].push_back(3)v[2].push_back(3)

步骤 2:无解判定(29~32 行)

cpp

运行

matlab 复制代码
if(v[i].size()%2){
    cout<<"-1";
    return;
}

题意约束:每一位上 1 的数量必须偶数,才能全部两两配对;奇数个无法配对,直接输出 - 1。

步骤 3:单个桶最小代价计算 ------get_min_step 核心逻辑

假设 vec 里是从小到大排好序的下标数组(存入时 i 从 1 到 n 遍历,天然有序),长度siz一定是偶数。两种配对方案:

方案 1 ans1:相邻顺次配对

配对组合:(vec[0],vec[1])、(vec[2],vec[3])、(vec[4],vec[5])...总代价 = 每一对右边减左边相加

cpp

运行

ini 复制代码
for(int i=0;i+1<siz;i+=2)
    ans1 += vec[i+1]-vec[i];

方案 2 ans2:错开配对(环形首尾相连)

配对组合:(vec[1],vec[2])、(vec[3],vec[4])...,最后单独一对 (vec.back(), vec[0]),环形距离 (n - vec.back()) + vec[0]

cpp

运行

ini 复制代码
for(int i=1;i+1<siz;i+=2)
    ans2 += vec[i+1]-vec[i];
if(siz!=0) ans2 += (n - vec[siz-1]) + vec[0];

最终取 min (ans1,ans2) 作为当前二进制位的最小代价

步骤 4:全局答案汇总

遍历 0~30 所有二进制位,把每一位算出的最小 temp 累加进 ans,最后输出 ans。

二、你不清楚的核心知识点

知识点 1:vector<int> v[32]; 的本质

  1. 不是标准二维 vector vector<vector<int>>
  2. 长度固定 32 的静态数组 ,数组每个元素是独立vector<int>
  3. 优点:直接按二进制位下标v[j]访问对应桶,写法简洁;
  4. 限制:行数固定 32 不能动态增减,不能整体拷贝赋值;
  5. 访问语法:支持v[i][j],等价(v[i])[j],先取第 i 个 vector,再取内部第 j 个下标。

知识点 2:vector 两种下标访问区别

  • v[i][j]:越界无报错,静默未定义行为;
  • v[i].at(j):越界抛出异常,调试更安全。

知识点 3:bitset 拆分二进制位

bitset<32> bt(a[i]) 快速取出数字每一位,bt[j] 直接判断第 j 位是否为 1,比位运算x & (1<<j)写法更简洁。

知识点 4:环形数组配对的两种最优模型

偶数个有序点环形配对只有两种最小配对方式:相邻配对 / 错开首尾配对,只需要算两者最小值,不用暴力枚举所有配对(暴力会超时)。

知识点 5:全局数组栈 / 堆分配区分

  • int a[500005] 开在全局区,不会栈溢出;如果写在函数内会爆栈;
  • vector<int> v[32] 数组本体在全局区,每个 vector 的数据存储在堆内存。

三、代码隐藏坑点(容易出错的地方)

  1. #define int long long 宏定义替换所有 int 为 long long,防止差值累加溢出;
  2. 输入输出加速 ios::sync_with_stdio(false);cin.tie(0),大数据量下避免 TLE;
  3. 下标从 1 开始,环形距离计算要用n - vec.back() + vec[0],不能直接vec[0] - vec.back()(会出现负数);
  4. 桶为空siz=0时,ans2 不需要额外加环形代价,代码做了siz!=0判断规避。

四、整体解题思想总结

  • 分治思想:二进制位独立处理每一位之间互不影响,分开计算代价再求和;
  • 贪心思想:有序偶数点环形配对仅两种最优方案,不用 DP / 暴力,线性遍历即可算出最小值;
  • 分桶思想:静态数组套 vector,快速按二进制位归类下标,实现 O (n*32) 线性复杂度。

五、代码实现

ini 复制代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
int n;
int a[500005];
vector<int> v[32];

int get_min_step(vector<int> vec){
    int siz=vec.size();
    int ans1=0,ans2=0;
    for(int i=0;i+1<siz;i+=2){
        ans1+=(vec[i+1]-vec[i]);
    }
    for(int i=1;i+1<siz;i+=2){
        ans2+=(vec[i+1]-vec[i]);
    }
    if(siz!=0) ans2+=(n-vec[siz-1])+(vec[0]);
    return min(ans1,ans2);
}

void solve(){
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++){
        bitset<32> bt(a[i]);
        for(int j=0;j<=30;j++){
            if(bt[j]) v[j].push_back(i);
        }
    }
    int ans=0;
    for(int i=0;i<=30;i++){
        if(v[i].size()%2){
            cout<<"-1";
            return;
        }
        int temp=get_min_step(v[i]);
        ans+=temp;
    }
    cout<<ans<<endl;
}

signed main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    solve();
}
相关推荐
Rust研习社2 小时前
这 8 个 Rust 学习资源值得每个新手收藏起来
后端·rust·编程语言
怕浪猫4 小时前
Electron 系列文章封面图
算法·架构·前端框架
Moonbit5 小时前
MoonBit ×CCF开源创新大赛 倒计时24天!快来提交你的作品
程序员·编程语言
徐小夕6 小时前
JitWord 3.0 正式发布,高精度Word异构解析+复杂组件兼容,打造web端协同Word编辑器
前端·vue.js·算法
通信小呆呆21 小时前
当算法有了“五感”:多模态数据融合如何向人体感官协同学习?
人工智能·学习·算法·机器学习·机器人
benben0441 天前
强化学习之DQN算法族(基于gymnasium开发)
算法
何以解忧,唯有..1 天前
Go语言循环语句详解:for、range与循环控制
开发语言·算法·golang
想吃火锅10051 天前
【leetcode】88.合并两个有序数组js
算法