STL的bitset可以比较方便的实现一些用到位运算技巧的算法,而且在空间复杂度和时间复杂度上比bool数组好很多,(虽然现在还是不知道bitset.count()的复杂度到底是多少),有一些题目可以用位运算优化的就可以用到它。
A - Explosion HDU - 5036
有很多门,门内有不定量的其他门的钥匙,你有对应的钥匙才能打开对应的门,当你没有任何钥匙时,会随机暴力打开某一扇没开的门,问暴力开门的次数的期望。
可以通过计算每一扇门被暴力打开的概率来计算总次数的期望,先处理出每一扇门被打开后不再暴力开门可以直接或间接获得打开哪些门,就可以处理出每扇门被打开的方案数量(包括暴力打开这扇门),所以概率就是1/方案数,用bitset的按位与进行处理。
#include <bits/stdc++.h>
using namespace std;
const int maxn=1005;
int n;
bitset<maxn> p[maxn];
int main()
{
int T,a,b;
int cas=0;
scanf("%d",&T);
while(T--){
cas++;
scanf("%d",&n);
for(int i=0;i<n;i++)p[i].reset(),p[i][i]=1;
for(int i=0;i<n;i++){
scanf("%d",&a);
while(a--){
scanf("%d",&b);
p[i][--b]=1;
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
if(p[j][i]){
p[j]|=p[i];
}
}
}
double ans=0;
for(int i=0;i<n;i++){
int cc=0;
for(int j=0;j<n;j++){
if(p[j][i])cc++;
}
ans+=1.0/cc;
}
printf("Case #%d: ",cas);
printf("%.5lf\n",ans);
}
return 0;
}
B - Bipartite Graph HDU - 5313
把一张可能不完整的二分图加边变成完整的二分图能加的最多边数。
满足二分图,可加的边就是两个点集的任意点对,所以对于n各点,点的分集合方案越接近n/2就总边数就越大,所以对于原图的每一个联通块求二份点集,问题就转化成了每一个联通块的两个点集如何组合,使得最终两个点集的点数量最接近n/2,dp转移复杂度比较高,使用位移操作可以降低一个n的复杂度。
#include<bits/stdc++.h>
using namespace std;
const int maxn=10015;
const int maxe=300005;
int n,m;
struct edge{
int to,nxt;
}e[maxe];
int head[maxn];
int tot;
void adde(int u,int v){
e[tot].to=v;
e[tot].nxt=head[u];
head[u]=tot++;
}
bitset<maxn> dp;
int cor[maxn];
int cc[2];
void bitpart(int cnt,int color){
cc[color-1]++;
cor[cnt]=color;
for(int i=head[cnt];i!=-1;i=e[i].nxt){
int v=e[i].to;
if(cor[v]==0){
bitpart(v,3-color);
}
}
}
int main(){
int T,a,b;
scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
dp.reset();
memset(head,-1,sizeof(head));
memset(cor,0,sizeof(cor));
tot=0;
for(int i=0;i<m;i++){
scanf("%d%d",&a,&b);
adde(a,b);
adde(b,a);
}
dp[0]=1;
for(int i=1;i<=n;i++){
if(cor[i]==0){
cc[0]=cc[1]=0;
bitpart(i,1);
dp=(dp<<cc[0])|(dp<<cc[1]);
}
}
int ans=0;
for(int i=0;;i++){
if(dp[n/2-i]){
ans=n/2-i;
break;
}
}
ans=ans*(n-ans)-m;
printf("%d\n",ans);
}
return 0;
}
C - Matrix multiplication HDU - 4920
矩阵乘法模3;
根据运算性质现对原矩阵求模,变成两个只有0,1,2的矩阵,根据矩阵运算性质,c[i][j]=sigma(a[i][k]*b[k][j]),对于0,1,2这三个数字,只有1*1,1*2,2*1,2*2会对mod3答案产生贡献,用bieset分别统计a矩阵每行为1,2的状态和b矩阵每列的状态,然后使用按位与和.count()运算统计答案。
#include<bits/stdc++.h>
using namespace std;
const int maxn=805;
const int maxe=300005;
int n,m;
bitset<maxn> a[maxn][3],b[maxn][4];
int getans(int i,int j){
int aa=(a[i][1]&b[j][1]).count();
int bb=(a[i][1]&b[j][2]).count();
int cc=(a[i][2]&b[j][1]).count();
int dd=(a[i][2]&b[j][2]).count();
return (aa+bb*2+cc*2+dd)%3;
}
int main(){
int x;
while(~scanf("%d",&n)){
for(int i=0;i<n;i++){
a[i][0].reset(),a[i][1].reset(),a[i][2].reset();
b[i][0].reset(),b[i][1].reset(),b[i][2].reset();
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
scanf("%d",&x);
a[i][x%3][j]=1;
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
scanf("%d",&x);
b[j][x%3][i]=1;
}
}
for(int i=0;i<n;i++){
for(int j=0;j<n;j++){
printf("%d",getans(i,j));
if(j<n-1)printf(" ");
}
printf("\n");
}
}
return 0;
}
D - Set Operation POJ - 2443
给定多个集合,让你判断数字i和j是否同时存在于这里面的某个集合中。
为每一个数字单独开一个bitset记录哪些集合里有这个数字,然后对于任意询问i和j,将bitset i和 bitset j 按位与,便可得出是否有两个集合同时有这个数字。
#include<cstdio>
#include<bitset>
using namespace std;
const int maxn=10005;
const int maxm=1005;
int n,m,q;
bitset<maxm> p[maxn];
int main(){
int a,b,c;
scanf("%d",&n);
for(int i=0;i<maxn;i++)p[i].reset();
for(int i=0;i<n;i++){
scanf("%d",&a);
while(a--){
scanf("%d",&b);
p[b][i]=1;
}
}
scanf("%d",&q);
while(q--){
scanf("%d%d",&a,&b);
bool f=0;
if((p[a]&p[b]).count())printf("Yes\n");
else printf("No\n");
}
return 0;
}