个人总结-pandas

加密算法的重要性以及了解加密算法分类

  返回  

位运算详细题解 - 2018CCPC桂林站:D. Bits Reverse

2021/7/21 4:23:29 浏览:

位运算题解

题源:2018CCPC桂林站:D. Bits Reverse

题目描述

在这里插入图片描述

题意理解

输入 T T T 组数据,每组数据含有两个整数 x , y x,y x,y,表示为二进制时,进行连续三位的反转操作,例如 ( 0 , 0 , 1 ) (0,0,1) (0,0,1) 反转为 ( 1 , 0 , 0 ) (1,0,0) (1,0,0) ;求最少经过几次反转操作,可以使得反转后的整数 x , y x,y x,y 相等;输出最少反准次数,如果无解,输出 − 1 -1 1

数据范围为: T   [ 1 , 1 0 4 ] T \ [1,10^4] T [1,104] x , y   [ 1 , 1 0 18 ] x,y \ [1,10^{18}] x,y [1,1018]

输出格式为: C a s e 1 : − 1 Case \quad 1: \quad -1 Case1:1



原理分析

首先,反转操作的本质是隔位交换,具有如下性质

  • 反转交换对中位完全无影响。
  • 反转交换时,邻位相互独立,反转步数可奇偶位分别计算。

其次,明确 x , y x,y x,y 最小反转次数后相等的条件

  • x , y x,y x,y 奇偶隔位上的 1 1 1 数量分别相等。
  • x , y x,y x,y 奇偶位上的 1 1 1 分别向对方最邻近奇偶位上反转移动。

所以,总体思路为首先判断 x , y x,y x,y 二进制位上的隔位 1 1 1 数量是否相等,不相等可直接否定,相等时进行反转次数的运算,运算时注意向最邻近位*反转移动至相等。


代码实现

边界限制为, T   [ 1 , 1 0 4 ] T \ [1,10^4] T [1,104] x , y   [ 1 , 1 0 18 ] x,y \ [1,10^{18}] x,y [1,1018]

  • T T T i n t int int
  • x , y x,y x,y l o n g   l o n g   i n t long \ long \ int long long int

首先开两个 v e c t o r vector vector 分别记录 x , y x,y x,y 1 1 1 的个数和位置,个数不相同时直接否定

#include <bits/stdc++.h>
#define lowbit(x) (x&-x)  //宏定义 lowbit 操作
typedef long long LL;     //定义 long long 数据类型
using namespace std;
LL T,res;  //定义全局变量 组数 T 结果 res 

//位运算函数 bool move(LL x, LL,y)
//在 res 中记录反转次数结果,并返回是否可进行
bool move(LL x,LL y)
{
	vector<LL> a,b;  //开两个 vector 记录 x,y 中的 1 的个数和位置
	if(!x||!y) return false; //如果 x,y 其中有 0,直接否定
	while(x&&y)
	{   
        // lowbit 拆分 x,y 的每一个 1 的位置 
		LL ta=lowbit(x);  
		LL tb=lowbit(y);
		x=x-ta;
		y=y-tb;
        
        // vector 中 push 每一个 1 在第几位
		LL cnta=0,cntb=0;
		while(ta)
		{
			ta=ta>>1;
			cnta++;
		}
		while(tb)
		{
			tb=tb>>1;
			cntb++;
		}
		a.push_back(cnta);
		b.push_back(cntb);
        
        //如果 x,y 中有一方先变成 0 ,则表示 x,y 中 1 的个数不同,直接否定
        //如果 x,y 同时变为 0 ,则从循环条件 while(x&&y) 正常结束循环
		if(x==0||y==0)
		{
			if(x==y) break;
			else return false;
		}
	} 
	
// bool move ...

在未触发否定条件下,两个 v e c t o r vector vector 中已经记录了 x , y x,y x,y每一个 1 1 1 的位置,然后进行最小反转操作次数的计算;因为最小的条件是最邻近位,所以可以先对两个 v e c t o r vector vector 进行升序排序,然后以任一数为目标,操作另一个数的低位到高位分别向目标位的低位到高位移动,在移动过程中既可以保证总体移位数最小,同时如果任何一位无法移动至目标位时,则表示奇偶位 1 1 1 个数不相等,可以直接否定

// bool move...

res=0; //重置结果
	
    //以 vector a 为目标,对 vector b 进行操作,并记录最少操作次数 res
	for(auto i=a.begin();i!=a.end();i++)
	{
		LL c=0;
		for(auto j=b.begin();j!=b.end();j++)
		{
			LL t=abs(*i-*j);
			if(t%2!=0) continue;  //如果两个 1 位差不是 2 的倍数,则不能隔位交换到达
			b.erase(j);  //找到可以到达的位时,消除一个目标,并记录开关 c 
			c=1; 
			res=res+(t>>1);  //res 累加记录反转次数
			break;
		}
		if(c==0) return false;  //如果找不到目标位,则表明奇偶位 1 不相等,直接否定
	}
	
	return true;  //不触发否定条件时,记录结果 res ,并返回真值
}

最终,条件可行时, r e s res res 中记录了最少操作次数,并返回 t r u e true true ,否则返回 f a l s e false false 并输出 − 1 -1 1

完整代码如下:

#include <bits/stdc++.h>
#define lowbit(x) (x&-x)  //宏定义 lowbit 操作
typedef long long LL;     //定义 long long 数据类型
using namespace std;
LL T,res;  //定义全局变量 组数 T 结果 res 

//位运算函数 bool move(LL x, LL,y)
//在 res 中记录反转次数结果,并返回是否可进行
bool move(LL x,LL y)
{
	vector<LL> a,b;  //开两个 vector 记录 x,y 中的 1 的个数和位置
	if(!x||!y) return false; //如果 x,y 其中有 0,直接否定
	while(x&&y)
	{   
        // lowbit 拆分 x,y 的每一个 1 的位置 
		LL ta=lowbit(x);  
		LL tb=lowbit(y);
		x=x-ta;
		y=y-tb;
        
        // vector 中 push 每一个 1 在第几位
		LL cnta=0,cntb=0;
		while(ta)
		{
			ta=ta>>1;
			cnta++;
		}
		while(tb)
		{
			tb=tb>>1;
			cntb++;
		}
		a.push_back(cnta);
		b.push_back(cntb);
        
        //如果 x,y 中有一方先变成 0 ,则表示 x,y 中 1 的个数不同,直接否定
        //如果 x,y 同时变为 0 ,则从循环条件 while(x&&y) 正常结束循环
		if(x==0||y==0)
		{
			if(x==y) break;
			else return false;
		}
	}
	
	res=0; //重置结果
	
    //以 vector a 为目标,对 vector b 进行操作,并记录最少操作次数 res
	for(auto i=a.begin();i!=a.end();i++)
	{
		LL c=0;
		for(auto j=b.begin();j!=b.end();j++)
		{
			LL t=abs(*i-*j);
			if(t%2!=0) continue;  //如果两个 1 位差不是 2 的倍数,则不能隔位交换到达
			b.erase(j);  //找到可以到达的位时,消除一个目标,并记录开关 c 
			c=1; 
			res=res+(t>>1);  //res 累加记录反转次数
			break;
		}
		if(c==0) return false;  //如果找不到目标位,则表明奇偶位 1 不相等,直接否定
	}
	
	return true;  //不触发否定条件时,记录结果 res ,并返回真值
}

int main()
{
	cin.tie(0);
    sync_with_stdio(false);
    
	cin>>T;
	for(LL i=1;i<=T;i++)
	{
		LL x,y;
		cin>>x>>y;
		if(move(x,y)) cout<<"Case "<<i<<": "<<res<<endl;
		else cout<<"Case "<<i<<": "<<-1<<endl;
	}
	
	return 0;
} 



总结归纳

突破点: 三位反转的实质是隔位交换

易错点: 数据范围必须是 $long \ long \ int $

模板点: 位运算模板 l o w b i t lowbit lowbit 拆分

联系我们

如果您对我们的服务有兴趣,请及时和我们联系!

服务热线:18288888888
座机:18288888888
传真:
邮箱:888888@qq.com
地址:郑州市文化路红专路93号