題目
http://acm.ecnu.edu.cn/problem/3303/
題意:給定整數 a 和 b,輸出區間 [a,b] 中對應二進制表示含 1 的個數最多的整數。如果存在多個解,則輸出符合條件的最小的整數。
解題思路
由於數據量較大,因此窮舉只能過50%的小數據,ab數值一大就超時。
直觀地看,要讓區間[a, b]內1的個數最多,那就要在左界a的基礎上,在它的二進制串上儘可能增加1,同時不超過b。因爲要求輸出最小的整數,所以要對a的二進制串從低到高,嘗試讓處於低位的0變成1,直到超出b。
有兩種辦法:
- 如果某數字num低位有0, 可以通過讓num和num+1做或運算,將最低位變成1。這種方法不需要將ans顯式地轉爲二進制。注意相關變量保險起見要聲明爲unsigned long long,原因是a,b可以取到
263−1 ,在a基礎上運算可能導致數值越界。 - 思想同上,只是用bitset類來存放二進制數,用weight數組存每個二進制位對應的十進制數,從低到高掃描bitset,出現0時,不使用位運算而是對a直接累加上十進制數。注意變量也要聲明爲unsigned long long,因爲weight[64] =
264>(263−1) .
AC代碼
#include <bits/stdc++.h>
using namespace std;
typedef unsigned long long uLL;
uLL adding_solve(uLL a, uLL b)
{
uLL ans = a;
while ( (ans|(ans+1)) <= b ) //從低到高,嘗試讓低位0變成1
ans |= (ans+1);
return ans;
}
uLL bitset_solve(uLL a, uLL b)
{
uLL weight[64]; //每個二進制位對應的十進制數,必須是unsigned LL, 因爲2^64 > 2^63-1
weight[0] = 1;
for (int i = 1; i < 64; ++i)
weight[i] = weight[i-1] * 2;
uLL a, b, ans;
cin >> a >> b;
bitset<64> bits(a); //用a初始化bitset
ans = a;
for (int i = 0; i < 64; ++i)
{
if (bits[i] == 0)
{
ans += weight[i]; //用十進制加法,嘗試讓低位0變成1
if (ans > b) //回溯,終止
{
ans -= weight[i];
break;
}
}
}
return ans;
}
int main()
{
ios::sync_with_stdio(false);
int kase;
cin >> kase;
for (int t = 1; t <= kase; ++t)
{
uLL a, b;
cin >> a >> b;
uLL ans = bitset_solve(a, b);
cout << "Case " << t << ": " << ans << endl;
}
return 0;
}