(一)小红的数组操作

链接: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();
}
相关推荐
倒头就睡的小比特3 天前
算法竞赛C++常用的STL
c++·算法
小羊没烦恼!3 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
猎头南楼3 天前
知识社区推荐系统实践:新用户冷启动与长短期兴趣建模的挑战 资深推荐算法工程师
人工智能·深度学习·算法·机器学习
旖旎夜光3 天前
力控面试题 01.01: 判定字符是否唯一(位运算) —— 题解
c++·学习·算法·leetcode·力控
wzdark3 天前
大规模并行计算中的负载均衡算法研究4
算法
Because_of_Her13 天前
并查集-听课笔记
笔记·算法·并查集
码流子3 天前
高速公路安全监测实践:碰撞监测预警+物联网底座,从感知到处置的闭环
大数据·人工智能·物联网·算法·架构
another heaven3 天前
【算法/C++ MD5算法能否逆解码?原理、C++实现与同类哈希算法对比】
c++·算法·哈希算法
wzdark3 天前
从算法设计模式看编程思维的抽象能力4
算法
2601_962218613 天前
万象生鲜系统称重自动多退少补算法解决生鲜非标品痛点
大数据·数据库·人工智能·python·算法