• noip模拟赛 梦想


    题目描述

    LYK做了一个梦。

    这个梦是这样的,LYK是一个财主,有一个仆人在为LYK打工。

    不幸的是,又到了月末,到了给仆人发工资的时间。但这个仆人很奇怪,它可能想要至少x块钱,并且当LYK凑不出恰好x块钱时,它不会找零钱给LYK。

    LYK知道这个x一定是1~n之间的正整数。当然抠门的LYK只想付给它的仆人恰好x块钱。但LYK只有若干的金币,每个金币都价值一定数量的钱(注意任意两枚金币所代表的钱一定是不同的,且这个钱的个数一定是正整数)。LYK想带最少的金币,使得对于任意x,都能恰好拼出这么多钱。并且LYK想知道有多少携带金币的方案总数。

    具体可以看样例。

    输入格式(dream.in)

        第一行一个数n,如题意所示。

    输出格式(dream.out)

    输出两个数,第一个数表示LYK至少携带的金币个数,第二数表示方案总数。

    输入样例

    6

    输出样例

    3 2

    样例解释

    LYK需要至少带3枚金币,有两种方案,分别是{1,2,3},{1,2,4}来恰好得到任意的1~n之间的x。

    输入样例2

    10

    输出样例2

    4 8

    数据范围

    对于30%的数据n<=10。

    对于60%的数据n<=100。

    对于100%的数据n<=1000。

    分析:第一问很好处理,就是看n的二进制位上有多少个是1,第二问可以先考虑搜索,因为个数定了嘛,所以每次搜当前的和是多少,这一位数字从哪一个开始枚举,选了多少个数字,因为每个数字只能选一次,可以边递归边判断,方法和:传送门 差不多.

          其实可以发现这就是一道dp嘛,把搜索时的参数变成状态就好了:f[i][j][k]表示前i个数字,和为j,最大的一个数字为k的方案数,递推非常好想,主要是空间问题,滚动数组优化一下就好了.

    60分暴力:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int n, cnt, ans, f[21][1010][21];
    
    void init(int x)
    {
        for (int i = 1; i <= x; i *= 2)
        {
            cnt++;
            x -= i;
        }
        if (x)
            cnt++;
    }
    
    int dfs(int dep, int sum, int l)
    {
        int cntt = 0;
        if (f[dep][sum][l])
            return f[dep][sum][l];
        if (dep == cnt + 1)
        {
            if (sum >= n)
            cntt++; 
            return cntt;
        }
        for (int i = l; i <= sum + 1; i++)
                cntt += dfs(dep + 1, sum + i, i + 1);
        return f[dep][sum][l] = cntt;
    }
    
    int main()
    {
        scanf("%d", &n);
        init(n);
        printf("%d %d
    ", cnt,dfs(1,0,1));
    
        return 0;
    }

    正解:

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    
    using namespace std;
    
    int n, cnt, f[2][1010][1010], last, now, ans;
    
    void init(int x)
    {
        for (int i = 1; i <= x; i *= 2)
        {
            cnt++;
            x -= i;
        }
        if (x)
            cnt++;
    }
    
    int main()
    {
        scanf("%d", &n);
        init(n);
        last = 0, now = 1;
        f[0][1][1] = 1;
        for (int i = 1; i < cnt; i++)
        {
            for (int j = 1; j <= n; j++)
                for (int k = 1; k <= n; k++)
                    if (f[last][j][k])
                        for (int l = k + 1; l <= j + 1; l++)
                            f[now][min(n, j + l)][l] += f[last][j][k];
            swap(now, last);
        }
        for (int i = 1; i <= n; i++)
            ans += f[last][n][i];
        printf("%d %d
    ", cnt, ans);
    
        return 0;
    }
  • 相关阅读:
    如何在Linux下修改Mysql的用户(root)密码
    CentOS 6.x 如何升级 glibc 2.17
    Tensorflow不同版本要求与CUDA及CUDNN版本对应关系
    darkflow测试和训练yolo
    使用 Alibaba 的 Homebrew 镜像源进行加速
    树莓派开启SSH的N种方法
    Linux开启ssh服务
    在Linux中创建静态库.a和动态库.so
    linux下动态链接库(.so)的显式调用和隐式调用
    删除Win10资源管理器中的3D对象/音乐/视频文件夹
  • 原文地址:https://www.cnblogs.com/zbtrs/p/7757829.html
Copyright © 2020-2023  润新知