洛谷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)的大数据来说,比上面快两倍

真是想不通比赛的时候自己为什么要打暴搜

最后

看在我写了(3.5)个小时的份上,不点个赞吗?

完结撒花 (。・∀・)ノfafafa

我要拿金牌!
原文地址:https://www.cnblogs.com/jerrywang-blogs/p/CSP-2020_power.html