信息学奥赛一本通 1223:An Easy Problem | OpenJudge NOI 4.6 1455:An Easy Problem
【題目鏈接】
ybt 1223:An Easy Problem
OpenJudge NOI 4.6 1455:An Easy Problem
【題目考點】
1. 數制
2. 枚舉
【解題思路】
解法1:枚舉
要找比給定數字n大的最小的二進制表示中1的個數相同的數字。
先求出n在二進制表示下1的個數。
而后不斷枚舉比n大的整數,求出其二進制表示中1的個數。如果1的個數與n在二進制下1的個數相同,那么就是找到了比n大的最小的二進制表示中1的個數相同的數字。
分析算法最大復雜度:n最大是10610^6106,二進制下各位全是1的情況,需要枚舉的次數最多。
已知一個x位二進制數11?1?x\underbrace {11\cdots 1}_xx11?1??,各位都是1,其數值為2x?12^x-12x?1。
令2x?1≤1062^x-1 \le 10^62x?1≤106,有x≤log2(106+1)≈19.9x \le log_2(10^6+1)\approx 19.9x≤log2?(106+1)≈19.9,x最大為19。考慮比11?1?19\underbrace{11\cdots 1}_{19}1911?1??大的最小的也有19個1的二進制數字,為1011?1?1810\underbrace{11\cdots 1}_{18}101811?1??,相當于比1011?1?18?11?1?19=100?0?18=218=26214410\underbrace{11\cdots 1}_{18}-\underbrace{11\cdots 1}_{19}=1\underbrace{00\cdots 0}_{18} = 2^{18}=262144101811?1???1911?1??=11800?0??=218=262144。
即對于每個數字,至多枚舉262144次,即可得到解,每次枚舉拆分數字要循環10次。
對于每個數字,運算復雜度最大為10610^6106數量級。該算法可以支持在1秒內對10~100個數字求比給定數字大的最小的二進制表示中1的個數相同的數字。題目沒說要輸入多少數字,默認不說就是小于等于100個。所以該算法是可行的。
解法2:找規律
思考,如果該二進制數末尾幾位是0,增加1后,其中1的個數就多了1個。再增加1,1的個數始終是變多的。如果數值增加后改變的位置其原位置上都是0,那么1的數量就不會減少到與原有1的數量相同。
原數字:1000
加1:1001
加1:1010
加1:1011
1的數量始終比1000要多。
只有當增加的數值與原位置上的1加和引發進位,才會使1的數量減少
原數字:1011
加1:1100:減少兩位1,增加一個進位的1。總共減少一位1。
觀察規律可知,第一次1減少的時機發生在最末尾一段連續的1發生進位的時候。
嚴格描述為:二進制數字d1d2?dx?x011?1?k00?0?m\underbrace{d_1d_2\cdots d_x}_x0\underbrace{11\cdots 1}_{k}\underbrace{00\cdots 0}_{m}xd1?d2??dx???0k11?1??m00?0??(d1~dxd_1\sim d_xd1?~dx?為任意數字。其中x,m最小為0,k最小為1)
該數字在增加100?0?m1\underbrace{00\cdots 0}_{m}1m00?0??后發生進位,成為d1d2?dx?x100?0?k+m\underbrace{d_1d_2\cdots d_x}_x1\underbrace{00\cdots 0}_{k+m}xd1?d2??dx???1k+m00?0??,這次進位增加了1位1,減少了k位1,所以共減少k-1位1。
為了讓1的數量不發生改變,應該在末尾增加k-1位1,使數字變為:d1d2?dx?x100?0?m+111?1?k?1\underbrace{d_1d_2\cdots d_x}_x1\underbrace{00\cdots 0}_{m+1}\underbrace{11\cdots 1}_{k-1}xd1?d2??dx???1m+100?0??k?111?1??
具體做法為
該算法針對每個數字,只是對數字各位進行幾遍遍歷。小于等于10610^6106的數字的二進制位為20位左右。因而針對每個數字的運算次數都是101010數量級。復雜度遠小于方法1。
【題解代碼】
解法1:枚舉
#include<bits/stdc++.h> using namespace std; int main() {int n, ans;while(cin >> n && n != 0){int ct_n = 0, ct_a;//ct_n:n中1的個數 ct_a:ans中1的個數 for(int a = n; a > 0; a /= 2)if(a%2 == 1)ct_n++;for(ans = n+1; true; ans++){ct_a = 0;for(int a = ans; a > 0; a /= 2)if(a%2 == 1)ct_a++;if(ct_a == ct_n){cout<< ans << endl;break;}}}return 0; }解法2:找規律(復雜度低)
//找最末的連續k個1,變為1加上k個0,末尾再添加k-1個1 #include <bits/stdc++.h> using namespace std; int main() {int n, num[50];while(cin >> n && n != 0){memset(num, 0, sizeof(num));int b, ct = 0, ni = 0, ans = 0;//b:最末連續的1的起始位置for(int a = n; a > 0; a /= 2)num[++ni] = a % 2;for(int i = 1; i <= ni; ++i){if(num[i] == 1 && num[i+1] == 0){b = i;break;}}num[b+1] = 1;if(b+1 > ni)ni = b+1;for(int i = b; num[i] != 0; --i){ct++;num[i] = 0;}for(int i = 1; i <= ct-1; ++i)num[i] = 1;for(int i = ni; i >= 1; --i)ans = ans*2+num[i];cout << ans << endl;} return 0; }總結
以上是生活随笔為你收集整理的信息学奥赛一本通 1223:An Easy Problem | OpenJudge NOI 4.6 1455:An Easy Problem的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 信息学奥赛一本通 1309:【例1.6】
- 下一篇: OpenJudge NOI 1.8 22