今天开始练习位运算,之前都是看课件,今天开始实战。
位操作(POJ - 3748)
其实这是道位运算的基本题,但是由于我之前并没有写过位运算,所以把思路想的特别复杂。又是循环又是加减,事实这些都可以运用左移和取反来操作。除此之外还有,在读入输入十六进制数时可以用%x。
第一个操作是将第x位变成0,我们就可以并一个数,这个数的第x位是0,其他全是1.即~(1<< x)。第二个操作分成两步,一步是将第y-2位变成0,这一步同第一个操作。另一步是将y和y-1位变成1,即或两个数,这两个数的第y位和第y-1位分别是1,其他数是0.即1<< y,1<< (y-1)。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int x,y,n;
int main()
{
scanf("%x,%d,%d",&n,&x,&y);
n=n&(~(1<<x));
n=n&(~(1<<(y-2)));
n=n|(1<<y)|(1<<(y-1));
printf("%x",n);
}
Binary Number (HDU - 3711)
题目大意:定义一种运算f,f(a,b)表示a和b的二进制数有几位是不同的。同时给定A,B两个集合,对于每一个B集合中的元素,需在A中找到一个元素和这个元素的f最小,如有多个则输出最小的一个。
因为这里的数据不大,可以直接暴力做,对于B中的每一元素进行枚举,用异或运算来判断两个元素用多少位相等,然后去最小的就可以。
同时要注意一个细节,输入中给定的A集合是无序的,我们要进行一次排序,我就是因为这个地方错了三次。而且异或之后求1的个数是有简便方法的,不需要对每一位进行操作。至于简便方法的实现就看我的代码。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int T,n,m;
int a[105],b[105];
int main()
{
scanf("%d",&T);
while(T--)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+n+1);
for(int i=1;i<=m;i++)
scanf("%d",&b[i]);
for(int i=1;i<=m;i++)
{
int mine=0x3fffffff,t;
for(int j=1;j<=n;j++)
{
int cnt=0;
int now=a[j]^b[i];
while(now)//这个就是求有多少个1的简便方法
{
now=now&(now-1);
cnt++;
}
if(cnt<mine)
mine=cnt,t=a[j];
}
printf("%d\n",t);
}
}
}
An Easy Problem (POJ - 2453)
题目大意:给定一个数n,求一个比n大而且二进制数中的1的个数和n相等。
有上一题的基础这道题就很简单,只需每次加1,用上道题的方法判断就可以。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,cnt;
int main()
{
while(1)
{
scanf("%d",&n);
if(n==0)
break;
cnt=0;
int t=n;
while(t)
{
t&=t-1;
cnt++;
}
for(int i=1;;i++)
{
int now=0;
t=n+i;
while(t)
{
t&=t-1;
now++;
}
if(now==cnt)
{
printf("%d\n",n+i);
break;
}
}
}
}
N皇后问题 (HDU - 2553)
这道题的位运算实现真的非常巧妙,用三个变量的二进制表示当前行可放的位置,然后进行枚举,推算出下一行可以放的位置。
在我的代码中h表示当前这一行可以放的位置,l和r分别表示主对角线和副对角线,三个量进行或运算取反在与总的方案数((1<< n)-1)并,就得到了当前行可以放的位置。
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,ans;
void dfs(int h,int l,int r)
{
int t=(1<<n)-1;
if(h==t)
{
ans++;
return;
}
int p=t&(~(h|l|r));
while(p)
{
int now=p&-p;
p-=now;
dfs(h+now,(l+now)>>1,(r+now)<<1);
}
}
int main()
{
while(1)
{
scanf("%d",&n);
if(n==0)
break;
ans=0;
dfs(0,0,0);
printf("%d\n",ans);
}
}
The Number of set (HDU - 3006 )
题目大意:给定n个集合,求这些集合能组成多少个不同的集合。
因为每一个元素都不超过m,而且m最大为14,所以可以直接用二进制将一个集合表示出来。然后与之前所有的情况做或运算,得到的新集合打上标记,最后再来统计一共有多少个标记就可以了。
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN (1<<14)+10
using namespace std;
int n,m,ans;
bool vis[MAXN];
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
ans=0;
memset(vis,0,sizeof vis);
for(int i=1;i<=n;i++)
{
int t;
scanf("%d",&t);
int now=0;
for(int j=1;j<=t;j++)
{
int x;
scanf("%d",&x);
now|=(1<<(x-1));
}
vis[now]=1;
for(int j=1;j<=MAXN;j++)
if(vis[j])
vis[j|now]=1;
}
for(int i=1;i<=MAXN;i++)
if(vis[i])
ans++;
printf("%d\n",ans);
}
}