容斥原理(二进制枚举)
在計數時,必須注意無一重復,無一遺漏。為了使重疊部分不被重復計算,人們研究出一種新的計數方法,這種方法的基本思想是:先不考慮重疊的情況,把包含于某內容中的所有對象的數目先計算出來,然后再把計數時重復計算的數目排斥出去,使得計算的結果既無遺漏又無重復,這種計數的方法稱為容斥原理。
兩個集合的容斥關系公式:A∪B = A+B – A∩B (∩:重合的部分)?
三個集合的容斥關系公式:A∪B∪C = A+B+C – A∩B – B∩C – C∩A +A∩B∩C?
詳細推理如下:?
1、 等式右邊改造 = {[(A+B – A∩B)+C – B∩C] – C∩A }+ A∩B∩C?
2、文氏圖分塊標記如右圖圖:1245構成A,2356構成B,4567構成C?
3、等式右邊()里指的是下圖的1+2+3+4+5+6六部分:?
那么A∪B∪C還缺部分7。?
4、等式右邊[]號里+C(4+5+6+7)后,相當于A∪B∪C多加了4+5+6三部分,?
減去B∩C(即5+6兩部分)后,還多加了部分4。?
5、等式右邊{}里減去C∩A (即4+5兩部分)后,A∪B∪C又多減了部分5,?
則加上A∩B∩C(即5)剛好是A∪B∪C。?
在這里我要說的是一個二進制枚舉。
正文
所謂二進制枚舉 就是一種暴力的方式,用0,1來代表一個數字存在或不存在。?
例如 0101 可以理解為 第一個物品要了,第二個不要,第三個要了,第四個不要。?
那么這個和容斥原理有什么關系呢??
我們都知道容斥原理的公式為:?
?
可能很多剛剛接觸的小伙伴不太了解,其實有很多的問題需要容斥來解決,我舉一個例子:?
如果求100以內的除了 2的倍數 和 3的倍數的數 有多少個,我們可以很容易知道 2的倍數的個數是100/2,而3的倍數是100/3,“答案“是100-50-33 = 17?
這顯然是不對的 以6為例 6既是2的倍數也是三的倍數,顯然減重了。 但是我們有人就要說了,我可以每個數都判斷一下。
但是,當n很大的時候,要O(n)時間復雜度。顯然是不符合實際的。
這里我先引入容斥原理的概念?
我們回到剛才的那個問題,我們有沒有辦法解決減重的情況??
這里我們就要運用容斥原理解決這個問題了,?
100-2的倍數-3的倍數+6的倍數?
減重復的加回來就好了,這個6是二和三的最小公倍數?
好了這個問題我們已經很好的解決了,但是我們真的解決問題了嗎??
我們在舉一個例子 :?
100以內的數 求 2 3 5 7 11 的倍數,我們還是用剛才方法,?
2的倍數+3的倍數+5的倍數+7的倍數+11的倍數- 。。。。。。。?
這個時候我們犯難了 要減的有2∩3,減2∩5......還要加回減多的2∩3∩5.....
太麻煩了,明明這么簡單的一道題,怎么解決??
這里我們就要引入二進制枚舉的概念了:?
根據上面的公式:
上面的公式有2^m-1種情況。
我們把二進制表示2,3,5,7,11五個數。為什么可以這樣呢?
這就是二進制枚舉的好處了。因為有5個數,所以5個數組成的倍數有2^5-1種。
比如2的倍數:我們表示為00001
? ? ? 2∩3的倍數:00011
? ? ? 5∩11的倍數:10100
就是說,1~2^5每一個二進制位(0表示沒選,1表示選定)表示2,3,5,7,11的選不選定。初學者還是有點難理解的,認認真真看代碼,看是怎么實現的,花點時間認真弄明白。
#include<iostream> #include<stdio.h> #include<math.h> using namespace std;int a[] = {2,3,5,7,11}; int main(){int n;int lena = 5;while(~scanf("%d",&n)){int ans = 0;for(int i = 1;i<(1<<lena);i++){ ///每個數都有2種狀態 ,在或不在 在本題中的意思是2,3,5,7,11的倍數或不是, ///一共有2^5-1種可能int tmp=0;int lcm=1;for(int j = 0;j<lena;j++){if((1<<j)&i){///選第j個b倍數子;與i的二進制的第j位比較,看是否為1,是則選中tmp++;//標志變量計算i中1的個數,也就是倍數的個數lcm*=a[j];///相乘 }}if(tmp%2==1) ans+=n/lcm; //奇數是加 偶數是減else ans-=n/lcm;printf("%d\n",ans);}cout<<ans<<endl;} }?
?
簡單應用:
由0 到9 數字組成排列,要求第一個數大于1,最一個數小于8,一共有幾種排列。?
思路:?
求逆問題,第一個數<=1,最后一個數>=8?
由容斥原理,?
則有 事件A(第一個數<=1) = 2*9!, 事件B(最后一個數>=8) = 2*9!, 事件C(A交B) C= 2*2*8!?
所以逆問題的結果是 A+B-C?
則問題的答案是 10!-(A+B-C)。
?
經典例題:
例題1:
給出一個數N,求1至N中,有多少個數不是2 3 5 7的倍數。 例如N = 10,只有1不是2 3 5 7的倍數。- 1
- 2
Input?
輸入1個數N(1 <= N <= 10^18)。?
Output?
輸出不是2 3 5 7的倍數的數共有多少。?
Sample Input
- 1
- 2
Sample Output
1- 1
- 2
包含排斥原理,就是那道公式。?
四個圓圈的并,是四個的單獨面積的總和,減去四個分別兩兩相交的兩層的面積,加上四個分別三三相交的三層的面積,再減去四個相交的面積。最后就是并了。這道題,也是這個意思,只不過數多一點。
?
#include<stdio.h> int main(){long long num=0,n,a,b,c,d,ab,ac,ad,bc,bd,cd,abc,abd,acd,bcd,abcd;scanf("%lld",&n);a=n/2;b=n/3;c=n/5;d=n/7;ab=n/6;ac=n/10;ad=n/14;bc=n/15;bd=n/21;cd=n/35;abc=n/30;abd=n/42;acd=n/70;bcd=n/105;abcd=n/210;num=a+b+c+d-ab-ac-ad-bc-bd-cd+abc+abd+acd+bcd-abcd;printf("%I64d\n",n-num); }
這次還有就是看數據量可不可以暴力過,可以的話,直接暴力就解決了。這就是考察你會不會進行估算。
自己可以用二進制枚舉寫一下。
?
總結
以上是生活随笔為你收集整理的容斥原理(二进制枚举)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: hadoop集群搭建 修改配置
- 下一篇: 2021年信用卡行业发展报告