初始i=s
每次:i=(i-1) & s
直到i=0
etc.
11000
10000
01000
00000
10000=10001 & 11000
01000=01111 & 11000
00000=00111 & 11000
etc.
11110
11100
11010
11000
10110
10100
10010
10000
01110
01100
01010
01000
00110
00100
00010
00000
证明:
所有i满足 (s & i)==i,
且所有满足 (s & i)==i 的数都出现过
1.
证明:(s & i)==i
因为“i=(i’-1) & s”,
对于i的第t位i(t):
当s(t)=0时,i(t)=_ & 0 = 0
_ & 0 = 0
当s(t)=1时
1 & i(t) = i(t)
所以(s & i)==i成立
2.
证明:
所有满足 (s & i)==i 的数都出现过
对于s中值为0的位 s(t),因为“i=(i’-1) & s”,所以i(t)永远为0
对于s中值位0的位 s(t),因为“i=(i’-1) & s”,所以i(t)永远不会因为s而影响,所以i(t)的值 为(i’-1)的第t位
即“(s & i)==i”相当于原来s中的‘1’组成的数字从11…1到00…0每次减少1的变化,而原来s中的‘0’永远不变
得证
Usage:
共有n个人,当前有m个人可以工作,每个人可以被选或不被选,求出这m个人的所有选择情况(用二进制表示)
Problem:
hiho1516
bfs+状态压缩:
重要的是弄清每次生成新的状态的方法。。。
1 #include <stdio.h> 2 #include <stdlib.h> 3 #include <stdbool.h> 4 5 struct node 6 { 7 long pos,step,sum; 8 }q[131073]; 9 //2^16 * 2(两岸) 10 11 long n,cond[3],g[17],num[65537][17];//0为起始岸;1为终止岸;2为船 总数(二进制) 12 long vis1[17],vis2[17],vis3; 13 bool vis[65537][2]; 14 15 bool judge() 16 { 17 long i,j,k; 18 for (j=0;j<3;j++) 19 { 20 for (i=0;i<g[cond[j]];i++) 21 { 22 k=num[cond[j]][i]; 23 if (((vis1[k] & cond[j])!=0) && ((vis2[k] & cond[j])==0)) 24 return false; 25 } 26 27 // for (i=0;i<n;i++) 28 // if (((cond[j]>>i) & i)!=1) 29 // if ( ((vis1[i] & cond[j])!=0) && ((vis2[i] & cond[j])==0) ) 30 // return false; 31 } 32 if ((cond[2] & vis3)==0) 33 return false; 34 return true; 35 } 36 37 int main() 38 { 39 long m,a,b,c,x,y,head,tail,i,j,k,total,pos,pos_,sum,step,mul[17]; 40 scanf("%ld%ld",&n,&m); 41 mul[0]=1; 42 for (i=1;i<=n;i++) 43 mul[i]=mul[i-1]<<1; 44 total=(1<<n)-1; 45 for (i=0;i<=total;i++) 46 { 47 g[i]=0; 48 j=i; 49 k=0; 50 while (j>0) 51 { 52 if ((j & 1)==1) 53 { 54 num[i][g[i]]=k; 55 g[i]++; 56 } 57 k++; 58 j=j>>1; 59 } 60 } 61 62 scanf("%ld%ld%ld",&a,&b,&c); 63 for (i=0;i<n;i++) 64 { 65 vis1[i]=0; 66 vis2[i]=0; 67 } 68 vis3=0; 69 for (i=1;i<=a;i++) 70 { 71 scanf("%ld%ld",&x,&y); 72 vis1[x]+=1<<y; 73 } 74 for (i=1;i<=b;i++) 75 { 76 scanf("%ld%ld",&x,&y); 77 vis2[y]+=1<<x; 78 } 79 for (i=1;i<=c;i++) 80 { 81 scanf("%ld",&x); 82 vis3+=1<<x; 83 } 84 for (i=0;i<65536;i++) 85 for (j=0;j<2;j++) 86 vis[i][j]=true; 87 88 head=0; 89 tail=1; 90 q[1].pos=0; 91 q[1].step=0; 92 q[1].sum=total; 93 vis[total][0]=false; 94 95 while (head<tail) 96 { 97 head++; 98 99 //另外的优化: 100 //如果在左边,则送尽量多的人到右边 101 //如果在右边,则送尽量少的人到左边 102 103 //编写程序的简单性:两岸做法的对称性 (Same),从而合二为一 104 105 pos=q[head].pos; 106 pos_=pos ^ 1; 107 sum=q[head].sum; 108 step=q[head].step+1; 109 //不必取0,前后必会发生变化 110 for (i=sum;i>0;i=(i-1) & sum) 111 if (g[i]<=m) 112 { 113 cond[2]=i; 114 cond[pos]=sum-i; 115 cond[pos_]=total-cond[pos]; 116 117 if (vis[cond[pos_]][pos_]==true && judge()==true) 118 { 119 // printf("%ld %ld ",cond[pos_],pos_); 120 if (pos_==1 && cond[1]==total) 121 { 122 printf("%ld ",step); 123 return 0; 124 } 125 tail++; 126 q[tail].pos=pos_; 127 q[tail].step=step; 128 q[tail].sum=cond[pos_]; 129 vis[cond[pos_]][pos_]=false; 130 } 131 } 132 } 133 printf("-1 "); 134 return 0; 135 }
/*
位运算的优势:
1.相比正常运算极快的运行速度
2.存储的减少
3.程序编写的简单(和不容易出错)
*/