• 洛谷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

    我要拿金牌!
  • 相关阅读:
    Jackson Annotation Examples
    Java 8 Stream API说明
    Spring Enable* 注解
    1568: [JSOI2008]Blue Mary开公司
    BZOJ1558: [JSOI2009]等差数列
    BZOJ3155: Preprefix sum
    BZOJ4552 [Tjoi2016&Heoi2016]排序
    BZOJ4653 [Noi2016]区间
    BZOJ4487 [Jsoi2015]染色问题
    Luogu2664 树上游戏
  • 原文地址:https://www.cnblogs.com/jerrywang-blogs/p/CSP-2020_power.html
Copyright © 2020-2023  润新知