首先感謝這位前輩的部落格……這裡詳細說下自己的理解。
這題主要是思路……
輸入兩個整數l和r,求兩個數滿足l<=x<=y<=r,使得x | y最大。
另ans = x | y,那麼ans肯定的位數肯定和r一樣,因為位數越多,這個數就越大,位數最多的肯定是r。
所以我們先把r用二進位制表示,把每一位存在乙個陣列裡面。
然後從二進位制的最右邊開始(**裡面因為是每次把這個數的二進位制最右邊的數拿出來放到陣列ans的第0,1,2,3的位置,所以遍歷的時候從0~anscnt)遍歷。
遇到某一位是1,說明這一位已經可以取到1,不用管;遇到0,說明這一位要試試能不能讓它進行或操作後變成1。
怎麼試呢?乙個個遍歷?資料範圍是10的18次方,肯定會超時。
所以我們這樣,假如ans陣列的第i個元素是0, 求乙個數num =r - sum - 1,sum表示ans中從0到i - 1的數字表示的值,比如ans的前4位是1,0, 1,0,那麼i=2的時候,sum就是
101這個二進位制數對應是十進位制數,也就是5 。
這個num就是與之進行或操作後可以讓這一位變成1的,舉個例子。假如r的二進位制是 11110111,現在要求能使那個0變成1的數,那麼所求的數肯定那一位就必須是1,所求的數
就是r - sum - 1,sum 就是111,r - sum = 11110000,然後再-1,11101111,可以看到對應的那一位是1 。
這個num一定比r小,因為sum >= 0,而num = r - sum - 1
這樣求出來的num是 滿足對應位是1 且 小於r 的數裡面最大的。因為求num的時候,r - sum,r左邊的幾位都沒有動,也不能動,只是右邊全變成了0,然後-1,這樣右邊全變
成了1,也就是最大,全1當然最大,對吧?(如果把前面的幾位也變了,會不會比num更大且滿足條件呢?不會的,因為如果把前面的0變成了1,那麼肯定就比r大了,如果把前
面的1變成0,又肯定比num小。舉個不太恰當的十進位制的例子,r=2345012,要把0變成9,那麼應該是2345012 - 12 - 1 = 2344999,如果把前面的2345任何乙個變大了,就肯定比r大,如果把4變成了3,那麼得2335999,比2344999小……)
為什麼要求出最大的滿足條件的數呢?因為要和 l(輸入的資料) 比較,如果最大的能使這一位變成1的數字都比l小,說明沒有數能使這一位變成1 。
如果num >= l,說明這一位可以變成1,就把ans陣列的這一位變成1 l;否則就不管
然後看下一位。
最後ans所表示的數就是答案。
可能有人懷疑,這樣每次算的num都不同,而題目規定只能取兩個數,會不會不對?
不會的。因為求最左邊那個0的時候,i = a, num的0到a - 1位都變成了1 。
那為什麼不一開始就求最左邊的0呢,因為這時求出來的num可能比l小,還是要看其他位。
#include#define len (64 + 5)
using namespace std;
long long l, r;
int ans[len];
int anscnt;
long long mi[len];
long long opmi(int num)
int main()
long long sum = 0;
for(i=0; i= l)
ans[i] = 1;
}else
sum += opmi(i);
} sum = 0;
for(i=0; iif(ans[i])
sum += opmi(i);
cout<} return 0;
}
HDU 5969 最大的位或
開始這道題一直想著直接位運算去遍歷,排除情況,但資料量還是很大,後來把兩個數二進位制所有位都分別放在兩個bool型陣列裡,就簡單多了,只需要考慮輸入的那兩個數從哪一位開始出現不同,小數也就是下界,其到上界之內一定會出現與下界位數相等並所有位數全1的數,所以以上界數為基礎,存在乙個數使它一段字尾或運算...
HDU 5969 最大的位或
題意 給定乙個區間,在這個區間內找到兩個數,使得他們的位或值最大。思路分析 我們可以從二進位制的角度來思考這個問題,我們有了乙個區間,那麼我們最終答案的二進位制的位數也就有了限制,那麼我們想要做的就一定是盡可能地把這些二進位制位全部變成1。由於我們一共選兩個數字或取最大值,區間最大值是一定要選的,因...
hdu 5969 最大的位或(貪心)
對於乙個l和r 我們都看成2進製 最優情況一定是1000和0111來異或這樣就能得到在位數不可能改變的情況下能夠得到的最大值,那麼如果l到r能夠存在這種情況就這麼異或 但如果l和r的位數相同,那麼就保留前面相同位數上的0和1,一旦遇到不同則按照以上方法對低位取 xx1000和 xx0111異或 in...