洛谷P7071—優秀的拆分(2020CSP-J第一題)
去考試的時候硬是將這道題打了個\(dfs\),只過了\(80\)%的資料,回家路上才突然醒悟這道題的正解思路。
原來此題這麼簡單
“看來是智商日常不線上吧”
—— gyh
題目傳送門
題面簡述
給定正整數\(n\),你需要判斷這個數的所有拆分中,是否存在優秀的
拆分。若存在,請你給出具體的拆分方案(從大到小輸出,空格隔開)。
優秀的拆分定義:
對於正整數\(n\)的一種特定拆分,我們稱它為“優秀的”,當且僅當在這種拆分下,\(n\)被分解為了若干個不同的2的正整數次冪。
正解思路
“任何一個正整數都可以拆分成若干個\(2\) 的非負整數次冪之和。”
——《小學奧數》
既然是\(2\)的非負整數次冪之和,我們可以直接將它轉換為\(2\)進位制
例如:
\(n=24_{10}=11000_{2}\)
然後從大到小輸出這一位是\(1\)的位權值
例如:
\(n=24_{10}=11000_{2}=16_{10}(10000_{2})+8_{10}(1000_{2})\)
輸出結果就是
\(16\) \(8\)
另外,所有的奇數都木有優秀的拆分
因為奇數包含\(2^0\)
0不是正整數(不會吧不會吧,不會真的有人不知道0不是正整數吧)
如果是奇數,直接輸出\(-1\)
上程式碼———
#include<iostream> #include<cstdio> using namespace std; int main() { freopen("power.in","r",stdin); freopen("power.out","w",stdout);//競賽千萬不要忘了這個 int n,i=0; bool w[50];//由於只有0與1,可以使用布林陣列儲存2進位制數 cin>>n; if(n&1)//等同於n%2==1,但是按位與運算速度更快 cout<<-1; else { while(n) w[i++]=n&1,n>>=1;//等同於w[i++]=n%2,n/=2; for(i=i-1;i>=0;i--) if(w[i]) cout<<(1<<i)<<" ";//輸出2^i } return 0;//不要忘了寫 }
其實還可以加一些優化,由於\(2^i\)的值是固定的,可以給\(2\)的非負整數次冪打一張表,輸出時可以直接呼叫
優化後的程式碼———
#include<iostream>
#include<cstdio>
using namespace std;
const int hh[50]={1,2,4,8,16,32,64,128,256,512,1024,2048,4096,8192,16384,32768,65536,131072,262144,524288,1048576,2097152,4194304,8388608,16777216};//最大到10^7,int可以存下
int main()
{
freopen("power.in","r",stdin);
freopen("power.out","w",stdout);
int n,i=0;
bool w[50];
cin>>n;
if(n&1)
cout<<-1;
else
{
while(n)
w[i++]=n&1,n>>=1;
for(i=i-1;i>=0;i--)
if(w[i])
cout<<hh[i]<<" ";//直接輸出2^i
}
return 0;
}
這樣速度就很快啦~~
時間複雜度是\(O(2logn)\)
其實還可以再優化啦~~
再優化就要靠對位運算的熟練運用
從大到小判斷\(n\)的這一位是不是\(1\),是\(1\)就輸出這一位的權值。
思路和上面差不多,但是可以省一個\(while\)迴圈
由於\(n\)最大是\(10^7\),可以直接從\(2^{24}\)開始列舉(\(2^{24}\)是最貼近\(10^7\)且小於\(10^7\)的\(2\)的非負整數次冪)
感謝來自\(Daddy\)的思路提供
上程式碼———
#include<iostream>
using namespace std;
int main()
{
int n;
cin>>n;
if(n&1)
cout<<-1;
else
for(int i=1<<24;i>0;i=i>>1)//i從2^24開始列舉,一直列舉到2^1
if(n&i)//如果這位是1
cout<<i<<" ";//輸出這一位的位權值
return 0;
}
主要程式(從第10行到第12行)一共會執行24次
時間複雜度是\(O(1)\)
對於一些不大於\(10^7\)的大資料來說,比上面快兩倍
真是想不通比賽的時候自己為什麼要打暴搜
最後