zoukankan      html  css  js  c++  java
  • 将一个整数分解为2的幂次的拆分数

    题目:https://acm.ecnu.edu.cn/problem/3034/

    一、开始思考

    找了一波规律,然后发现了如果n是奇数,那么f(n)=f(n-1)

    然后去搜索了一下拆分数,发现一个讲述了求正整数的所有拆分数的,和这个有些类似。引入一个拆分的组合中的最大数m,由此可以分为最大数包括这个m的组合与最大数不包括m的组合。f(n,m)表示和为n,最大数为m的拆分数。

    f(n,m) = f(n,m-1) + f(n-m,m)

    f(n,m-1)表示最大数肯定不是m的组合数; f(n-m,m)表示最大数肯定是m的组合数。

    类似的,对于本题可以得到推导式:

    f(n,m) = f(n,m/2) + f(n-m,m)

    对于n == m时:

    f(n,m) = f(n,m/2) + 1

    所有的情况有:

    if(n == 1 或者 m == 1)

      f(n,m) = 1

    if(n < m)

      f(n,m) = f(n,n)

    if(n == m)

      f(n,m) = f(n,m/2) + 1

    else:

      f(n,m) = f(n,m/2) + f(n-m,m)

    写代码时,注意这个m始终要保证是2的幂次数。

    版本1:

    /**
        求拆分数的递归实现
    */
    #include<bits/stdc++.h>
    using namespace std;
    //求得不大于x的最大的,可以由2的幂次得到的数
    int getV(int x){
        int t,cnt;
        t = x;
        cnt = 0;
        while(t!=0){
            t>>=1;
            cnt++;
        }
        cnt--;
        return (1<<cnt);
    }
    //核心递归计算
    int f_cal(int m,int n){
        if(m == 1|| n == 1)
            return 1;
        if(m < n){
            return f_cal(m,getV(m));
        }else if(n == m){
            return 1 + f_cal(m,getV(n/2));
        }
        //肯定有 n  和肯定没有n的组成
        return f_cal(m,getV(n/2)) + f_cal(m-n,n);
    }
    int main(){
        int T;
        //cal();
        cin >> T;
        int n,m;
        for(int i = 0;i < T;i++){
            cin >> m;
            n = getV(m);
            //n = getVM(m);
            cout << "case #" << i << ":" << endl;
            cout << f_cal(m,n) << endl;
            //cout << mm[m][n] << endl;
        }
        return 0;
    }

    会超时的。

    二、优化

    记忆化处理一下:

    版本2:

    /**
        求拆分数的递归实现
    */
    #include<bits/stdc++.h>
    using namespace std;
    unsigned long long mm[1000001][21];
    
    const int MN = 1000000000;
    //求得幂次
    int getVM(int x){
        int t,cnt;
        t = x;
        cnt = 0;
        while(t!=0){
            t>>=1;
            cnt++;
        }
        cnt--;
        return cnt;
    }
    //循环计算
    void cal(){
        for(int i = 0;i < 1000001;i++){
            mm[i][0] = 1;
        }
        for(int i = 0;i < 21;i++){
            mm[1][i] = 1;
        }
        for(int i = 2;i < 1000001;i++){
            for(int j = 1;j < 21;j++){
                if(i < (1 << j)){
                    mm[i][j] = mm[i][getVM(i)];
                }else if(i == (1 << j)){
                    mm[i][j] = (mm[i][j-1] % MN + 1) % MN;
                }else{
                    mm[i][j] = (mm[i][j-1] % MN + mm[i - (1<<j)][j] % MN) % MN;
                }
            }
        }
    }
    
    int main(){
        int T;
        cal();
        cin >> T;
        int n,m;
        for(int i = 0;i < T;i++){
            cin >> m;
    
            n = getVM(m);
            cout << "case #" << i << ":" << endl;
            //cout << f_cal(m,n) << endl;
            cout << mm[m][n] % MN << endl;
        }
        return 0;
    }

    这个代码已经AC了。看完代码会发现,为什么我会模1000000000???我在分析错误样例时发现,有的正确答案正好是我的答案的后半部分!??!??!我突然有了大胆的假设》《

    查了一下这个题,讨论区都是,有模?。。竟然是真的

    加上这个神奇的模

    顺利AC

     

    啥?为什么re了这么多次?因为,我在之前递归版本上直接记忆化,它就RE,不知道为啥。我不信这个邪,多交了几次,结果评测机赢了。

     

    三、简化代码

    在讨论区发现,有一个一维数组就解决了的!学习了一下。它的分配是根据组合中有没有1,重点就在于对这个的证明了。很完美。链接在下--

      【转】 原文链接:https://blog.csdn.net/zhang20072844/java/article/details/17033931

    改进效果:

    版本3:

    /**
        求拆分数的递归实现
    */
    #include<bits/stdc++.h>
    using namespace std;
    unsigned long long mm[1000001];
    
    const int MN = 1000000000;
    
    //循环计算
    void cal(){
        mm[0] = 1;
        for(int i = 1;i < 1000001;i++){
            if(i%2){
                mm[i] = mm[i-1];
            }else{
                //分为含有1和不含有1的组合
                mm[i] = (mm[i-1] + mm[i/2])%MN;
            }
        }
    }
    
    int main(){
        int T;
        cal();
        cin >> T;
        int n,m;
        for(int i = 0;i < T;i++){
            cin >> m;
            cout << "case #" << i << ":" << endl;
            //cout << f_cal(m,n) << endl;
            cout << mm[m] % MN << endl;
        }
        return 0;
    }


  • 相关阅读:
    心得体会,搞清楚你为什么学习C++?
    完整版本的推箱子小游戏,最简单的纯C语言打造
    联合体、枚举体初步了解及运用
    结构体的初步了解
    使用 Appium 测试微信小程序 Webview——打开调试功能
    Jmeter 使用ssh command 链接linux
    jmeter响应内容乱码问题
    Mac 更新 node版本
    解决jenkins + ant + jmeter发送邮件失败的问题:java.lang.ClassNotFoundException: javax.mail.internet.MimeMessage
    bash特殊字符-2
  • 原文地址:https://www.cnblogs.com/gudygudy/p/12929849.html
Copyright © 2011-2022 走看看