【位运算】起床困难综合症(包含错误思路点拨)
原題
解題思路一(錯誤):
順著做(每個數用數組存)的思路是人腦思路,但是電腦會TLE。我們可以反著來。
大概的思路就是先假設一個最終結果的最大值。
然后問電腦:這數運算回去(按照輸入的門的順序,逆著順序運算回去)在我的m范圍之內嗎?
在,我就得寸進尺,我用11000000試(看是否 <= m);否則用01000000作為ans嘗試。原則總是最高位優先,逐漸考慮低位。
錯誤原因:想當然。因為如a&b|c = d并不能得到 d|c&b = a,不能直接逆過去
?
解題思路二(超時):
#include <bits/stdc++.h> using namespace std; const int N=1e5+5; struct data{int op,k; }qry[N]; char s[10]; int n,m; int suan(int a){for(int i=1;i<=n;i++){if(qry[i].op==1) a&=qry[i].k;if(qry[i].op==2) a|=qry[i].k;if(qry[i].op==3) a^=qry[i].k;}return a; } int main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%s%d",s,&qry[i].k);if(s[0]=='A')qry[i].op=1;if(s[0]=='O')qry[i].op=2;if(s[0]=='X')qry[i].op=3;}int maxn=0;for(int i=0;i<=m;i++){ maxn = max(maxn,suan(i));}printf("%d\n",maxn);return 0; }思路就是遍歷從0到m,每次取結果的最大值。但部分超時。
分析時間復雜度,n <= 1e5,m <= 1e9,相乘是1e14,超時是肯定的。所以我們要優化想法。
?
可以這樣思考,比方說進門前的數字是1001?000,是10011000出門后比10010000出門后結果要大,那么10010000粗體0的低3位000無需考慮了!這是一個很關鍵的想法,因為位運算的每一位都是獨立的。10011000的出門結果比10010000大,則表明從右往左第4位應該選用1而不是0,這樣出門的結果對應的這一位也就是1.
那么,縱然第3位,10010000的輸出是111,也不如10011000的出門結果大了。這是一種貪心的想法,因為只要高位比較大,無論低位如何,肯定是高位大的那個數結果大。這樣就引入了下面的想法:
?
解題思路三(正確):
要盡量保持經過每個門運算之后,得到的結果最大。我們可以對通過門前的數進行逐位調整:在<m的前提下,如果某位是1使通過門后結果更大,那么就讓這一位是1;否則不變(默認是0).
這樣對最多31位進行考慮,運算的數量級和門的數量差一個常數,即1e5,不會超時。
//AC代碼 #include <bits/stdc++.h> using namespace std; const int N=1e5+5; struct data{int op,k; }qry[N]; char s[10]; int n,m; int cal(int a){for(int i=1;i<=n;i++){if(qry[i].op==1) a&=qry[i].k;if(qry[i].op==2) a|=qry[i].k;if(qry[i].op==3) a^=qry[i].k;}return a; } int main(){scanf("%d%d",&n,&m);for(int i = 1;i <= n;i++){scanf("%s%d",s,&qry[i].k);if(s[0]=='A') qry[i].op = 1;if(s[0]=='O') qry[i].op = 2;if(s[0]=='X') qry[i].op = 3;}int ans = 0;for(int i = 30;i >= 0;i--){ if(ans + (1 << i) > m) continue;if(cal(ans) < cal(ans + (1 << i))) ans += 1<<i;//如果入門前的數第i位是1時結果更大,則入門時的該位設置為1;否則不變 }printf("%d\n",cal(ans));return 0; }?
?
問:能否將① for(int i = 30;i >= 0;i--){
?? ??? ?if(ans + (1 << i) > m)?? ?continue;
?? ??? ?if(cal(ans) < cal(ans + (1 << i)))?? ?ans += 1<<i;
??? }
?
改成
②? for(int i = 0;i <= 30;i++){
?? ??? ?if(ans + (1 << i) > m)?? ?continue;
?? ??? ?if(cal(ans) < cal(ans + (1 << i)))?? ?ans += 1<<i;
??? }
也就是說從最低位開始找使此位的最大輸出的入門值?
?
答:不能!比方說可能最低位是1時得到該位的最大輸出了,同時最高位是1也是最高位的最大輸出。而這樣組合得到的數超過了m的范圍,就不符合題意,continue掉了。那么得到的答案就是一個較小的答案,這樣得到的最終輸出結果,是小于100000作為入門值的最終輸出。
因為:最終輸出的結果,高位大的則整個數大。所以我們很自然地要優先滿足高位輸出最大的要求。
比方說這樣一個例子:
輸入流為:
1 4
AND 5
| Span | Doors | Op |
| 000 | 1 | &101 |
| 001 | ? | ? |
| 010 | ||
| 011 | ||
| 100 |
在①寫法得到的結果是4;而在②寫法得到的結果是1.
原因已經解釋完畢。
總結
以上是生活随笔為你收集整理的【位运算】起床困难综合症(包含错误思路点拨)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【第二十三篇】Spring Boot集成
- 下一篇: go-resiliency源码解析之-t