2020年 1月3日 OJ习题【位运算&二进制枚举】

首先,要了解位运算
算数位运算
1、与(&):
对于指定的两个数A=60(0011 1100)
B=13(0000 1101)
执行一下操作 A&B=12(0000 1100)
就是对二进制每一位进行了一次与操作,同为1,结果为1,否则为0
2、或(|):
对于指定的两个数A=60(0011 1100)
B=13(0000 1101)
执行一下操作 A|B=61(0011 1101)
就是对二进制每一位进行了一次或操作,同为0,结果为0,否则为1
3、非 按位取反(~):
对于指定的一个数A=60(0011 1100)
执行以下操作 ~A=195(1100 0011)
就是对二进制每一位进行了一次取反操作,若二进制数位0,则变成1,否
则变成0
4、异或运算
异或,英文为exclusive OR,缩写成xor
异或(xor)是一个数学运算符。它应用于逻辑运算。异或的数学符号为“⊕”,计算机符号为“xor”。其运算法则为:
a⊕b = (¬a ∧ b) ∨ (a ∧¬b)
如果a、b两个值不相同,则异或结果为1。如果a、b两个值相同,异或结果为0。
异或也叫半加运算,其运算法则相当于不带进位的二进制加法:二进制下用1表示真,0表示假,则异或的运算法则为:0⊕0=0,1⊕0=1,0⊕1=1,1⊕1=0(同为0,异为1),这些法则与加法是相同的,只是不带进位。

对于异或一个非常重要的性质,对于一个值异或同一个值两次,则结果还是原值。

在c/c++中异或用^符号表示;
例如:
对于指定的两个数 A=60(0011 1100)
B=13(0000 1101)
执行一下操作 A^B=49(0011 0001)
就是对二进制每一位进行了一次异或操作,即非进位加法。

二进制移位操作符:
移位操作有两种左移与右移:
1、左移<<
例如:A=5(0101)
如果向左移动一位即A<<1结果为1010,十进制的10。二进制中的左移就是乘二操作,在c/c++中左移运算速度比乘二速度要快。
2、右移>>
例如:A=5(0101)
如果向右移动一位即A>>1结果为0010,十进制的2。二进制中的左移就是除二操作(舍去小数)。

二进制枚举

二进制枚举利用的是二进制下n位长度的数有2n个,一个有n个元素的集合子集个数也为2n个,所以可以利用二进制的1,0和集合中的元素联系起来他可以实现组合也可以实现容斥
对一个二进制来说1代表取这个元素,0代表不取这个元素,1和0所在的位置代表元素的位置,这样的思想在有时候给题目有了很大的方便举个例子如集合{a,b,c,d,e}
当二进制00000就代表什么都不取, 10000代表取a,01000代表取b,11000代 表取a,b如此
所以我们需要枚举的数量就是00000到11111,也就是0到1<<n位,<<代表左移操作

teacher Li

本题应考虑二进制异或,即对字符串的每一位进行异或,因为每个字符对应属于自己的ASICC码值,且根据题意,只有一位同学没来,也就说明,所有的字符串中,除了那个没来的同学,其他名字都出现了两遍,那样的话,从头到尾进行异或,最终得到的结果就会是没来的那位同学的名字

对于异或一个非常重要的性质,对于一个值异或同一个值两次,则结果还是原值。

一个数与0进行异或,其结果不变!!!

#include<bits/stdc++.h>
using namespace std;

int main()
{
	int n,i,j,k;
	char x[30],y[30];
	k=1;
	while(cin>>n>>x)
	{
		for(i=1;i<=2*n-2;i++)
		{
			cin>>y;
			for(j=0;j<max(strlen(x),strlen(y));j++)//这里要取长的字符串长度//
			{
				x[j]=x[j]^y[j];
			}
		}
		printf("Scenario #%d\n",k++);
		printf("%s\n\n",x);
	}
	return 0;
} 

Find different

本题由于n非常大,无法用数组装,因此直接输入每一个数,然后直接进行异或,想法与上一题相同!!!

#include<bits/stdc++.h>
using namespace std;

int main()
{
	long long n;
	int ans,i;
	while(scanf("%lld",&n)!=EOF)
	{
		ans=0;
		for(long long j=0;j<n;j++)
		{
			scanf("%d",&i);
			ans=ans^i;
		}
		printf("%d\n",ans);
	}
	return 0;
} 

和为K–二进制枚举

#include<bits/stdc++.h>
using namespace std;

int main()
{
	int n,flag,x[25];
	long long k,sum;
	while(cin>>n>>k)
	{
		flag=0;
		for(int i=0;i<25;i++)
		{
			x[i]=0;
		}
		for(int i=0;i<n;i++)
		{
			cin>>x[i];
		}
		for(int i=0;i<(1<<n);i++)
		{
			sum=0;
			for(int j=0;j<n;j++)
			{
				if(i&(1<<j))
				{
					sum+=x[j];
				}
			}
			if(k==sum)
			{
				flag=1;
				break;//找到一个符合就退出//
			}
		}
		if(flag==0)
			{
				cout<<"No"<<endl;
			}
		else
		{
			cout<<"Yes"<<endl;
		}
	} 
	return 0;
} 

陈老师加油-二进制枚举

#include<bits/stdc++.h>
using namespace std;

int main()
{
	int t,sum,k1,k2,T;//t是临时变量,记录每种方案的油量//
	while(cin>>T)
	{
		sum=0;
		for(int i=0;i<(1<<15);i++)
		{
			k1=k2=0;
			t=T;
			for(int j=0;j<15;j++)
			{
				if(i&(1<<j))
				{
					t*=2;
					k1++;
				}
				else
				{
					t-=1;
					k2++;
					if(t<=0)//油不能不大于0//
					break;
				}
			}
			if(k1==5&&k2==10&&t==0)
			{
				sum++;
			}
		}
		cout<<sum<<endl;
	}
	return 0;
} 

纸牌游戏-二进制-搜索

#include<bits/stdc++.h>
using namespace std;

int main()
{
	int n,p;
	int x[25],sum,count;
	while(cin>>n>>p)
	{
		for(int i=0;i<n;i++)
		{
			x[i]=0;
		}
		for(int i=0;i<n;i++)
		cin>>x[i];
		count=0;
		for(int i=0;i<(1<<n);i++)
		{
			sum=0;
			for(int j=0;j<n;j++)
			{
				if(i&(1<<j))
				{
					sum+=x[j];
				}
			}
			if(sum==p)
				count++;
		}
		cout<<count<<endl;
	}
	return 0;
} 

权利指数

#include<bits/stdc++.h>
using namespace std;

int main()
{
	int t,n,sum,add;//sum是总票数,add是同意的票数//
	int x[25],y[25];//x是票数数组,y是权利指数数组//
	while(cin>>t)
	{
		for(int i=0;i<t;i++)
		{
			sum=0;
			cin>>n;
			for(int i=0;i<n;i++)
				x[i]=y[i]=0;
			for(int i=0;i<n;i++)
			{
				cin>>x[i];
				sum+=x[i];
			}
			for(int i=0;i<(1<<n);i++)
			{
				add=0;
				for(int j=0;j<n;j++)
				{
					if(i&(1<<j))
					{
						add+=x[j];
					}
				}
				if(add>(sum/2))
				{
					for(int j=0;j<n;j++)
					{
						if(i&(1<<j))
						{
							add-=x[j];
							if(add<=(sum/2))//无论该团体是不是关键小团体,都得把剪掉的数再加回来//
								{
									y[j]++;
									add+=x[j];
								}
							else
							{
								add+=x[j];
							}
						}
					}
				}
			}
			for(int i=0;i<n-1;i++)
				cout<<y[i]<<" ";
				cout<<y[n-1]<<endl;
		}
	}
	return 0;
} 

趣味解题

本题的想法是先把每道题的ac,wa的概率都算出来,分别装到两个数组里,然后二进制枚举进行遍历,对于每种方案的概率是ac题的概率之积乘以wa题的概率之积,然后把所有ac题数符合题意的方案的概率相加

#include<bits/stdc++.h>
using namespace std;

int main()
{
	int t,n,x,k;
	double a[15],b[15],c[15],ac[15],wa[15],ans,ans1;
	while(scanf("%d",&t)!=EOF)
	{
		for(int i=0;i<t;i++)
		{
			scanf("%d",&n);
		for(int i=0;i<n;i++)
		{
			scanf("%lf",&a[i]);
		}	
		for(int i=0;i<n;i++)
		{
			scanf("%lf",&b[i]);
		}
		for(int i=0;i<n;i++)
		{
			scanf("%lf",&c[i]);
		}
		scanf("%d",&x);
		for(int i=0;i<n;i++)
		{
			wa[i]=(1.0-a[i])*(1.0-b[i])*(1.0-c[i]);
			ac[i]=1.0-wa[i];
		}
		ans=0;
		for(int i=0;i<(1<<n);i++)
		{
			k=0;
			ans1=1.0;
			for(int j=0;j<n;j++)
			{
				if(i&(1<<j))
				{
					ans1*=ac[j];
					k++;
				}
				else
				{
					ans1*=wa[j];
				}
			}
			if(k==x)
			{
				ans+=ans1;
			}
		}
		printf("%.4lf\n",ans);
		}
	} 
	return 0;
} 

四糸乃买花-二进制枚举

#include <bits/stdc++.h>
using namespace std;

int main()
{
	int n,k,count;
	long long a[25],w,w1;//w1记录每种方案剩下的钱// 
	cin>>n;
	for(int i=0;i<n;i++)
	cin>>a[i];
	cin>>w;
	count=0;
	for(int i=0;i<(1<<n);i++)
	{
		k=0;
		w1=w;
		for(int j=0;j<n;j++)
		{
			if(i&(1<<j))
			{
				w1-=a[j];
				k++;
			}
		}
		if(k%4==0&&w1%4==0&&k>0&&k<n&&w1>=0)//k不能为0,也不能为n,而且剩下的钱应为正或者正好为0,且k,w1都要是4的倍数// 
		{
			count++;
		}
	}
	cout<<count<<endl;
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值