Codeforces Gym 100531D Digits (暴力、打表)

本文介绍了一种通过暴力枚举找到具有相同数字之和的最小数字集合的方法。通过对不同数字之和进行计数并记录最小总和,有效地解决了问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这里写图片描述
题目链接
1.暴力打表。。。
上来队友开始推规律

n
1 1= 1
2 11= 1 + 10
3 33= 2 + 11 + 20
4 66= 3 + 12 + 21 + 30
5 110= 4 + 13 + 22 + 31 + 40
6 165= 5 + 14 + 23 + 32 + 41 + 50
7 231= 6 + 15 + 24 + 33 + 42 + 51 + 60
8 308= 7 + 16 + 25 + 34 + 43 + 52 + 61 + 70
9 396= 8 + 17 + 26 + 35 + 44 + 53 + 62 + 71 + 80
10 495= 9 + 18 + 27 + 36 + 45 + 54 + 63 + 72 + 81 + 90
11 603= 9 + 18 + 27 + 36 + 45 + 54 + 63 + 72 + 81 + 90 + 108
12 720= 9 + 18 + 27 + 36 + 45 + 54 + 63 + 72 + 81 + 90 + 108 + 117
推到这里,队友说之后的只能是各位数字之和为9的数加起来和最小

于是我简单的打了个表

int cnt=0;
ll  sum=0;
for(int i=1; i<=10000000; i++)
{
    if(getsum(i) == 9)  //getsum()是求各位数字之和
    {
         sum+=i;
         cnt++;
         book[cnt] = sum;
    }
    if(cnt == 5000) break;
}

如此暴力的把n=10以后的答案都算出来了。。
结果WA在了第八组上
我开始debug。。。一直觉得题面给的除以9是个暗示。。。
所以改了挺长时间也没出,直到我觉得需要试一下各位数之和等于别的情况修改getsum(i) == 15 book[5000]小了很多
当getsum(i)==10 book[39] 就比 个位数字之和等于9的book[39]小

所以我有了个大胆的想法= = 我试了一些数 发现当getsum(i)==20 时
或者再往后 它的book[5000]开始不再变小,开始稳定不变。
代码就出来了
。。。略微暴力
最坏的求各位数字之和是分解十次 所以最坏的时间复杂度是10^8次幂吧
但是由于有剪枝和大多数数字到不了十位,所以还是会好很多。

#include <iostream>
#include <cstring>
#include <cstdio>
#define ll long long
using namespace std;
ll book[5005];
ll getsum(int x)
{
    int sum=0;
    while(x)
    {
        sum+=x%10;
        x = x/10;
    }
    return sum;
}
int main()
{
    int n;
    for(int k=9; k<=20; k++)
    {
        int cnt=0;
        ll  sum=0;
        for(int i=1; i<=10000000; i++)
        {
            if(getsum(i) == k)
            {
                sum+=i;
                cnt++;
                if(k==9)
                    book[cnt] = sum;
                else book[cnt] = min(book[cnt],sum);
            }
            if(cnt == 5000) break;
        }
    }
    book[1] = 1;
    book[2] = 11;
    book[3] = 33;
    book[4] = 66;
    book[5] = 110;
    book[6] = 165;
    book[7] = 231;
    book[8] = 308;
    book[9] = 396;
    book[10] = 495;
    //freopen("digits.in", "r", stdin);
    //freopen("digits.out", "w", stdout);
    while(cin>>n)
    {
        printf("%lld\n",book[n]);
    }
    return 0;
}

2.来自大牛们的科学暴力法:
来源博客

这个的思路就是存下 在1到100000中 每种各位数字之和的出现次数,并在结构体中存下这个各位数字之和的最小和 。
然后输入n 匹配次数。 他在测试中得到正确答案也一定是测试了很多范围最终确定100000

    /********************************************************  
    题意:给你一个n表示需要你找n个数字,每个数字的各个位数之 
    和必须相等,求最小的n个数字之和 
    类型:暴力  
    分析:因为n个数字都必须是正整数,由n<=5000, 暴力测试发现 
    1~100000以内可以得到5000个正解,由1~100000每个数字最大分 
    解次数<=10,暴力解决这道题的复杂度<10^6 
    *********************************************************/    
    #include<stdio.h>  
    #include<algorithm>  
    #include<iostream>  
    #include<cmath>  
    #include<cstring>  
    using namespace std;  
    struct node{  
        long long sum;  
        int cishu;  
    }a[100];  
    int main(){  
        //freopen("digits.in","r",stdin);  
        //freopen("digits.out","w",stdout);  
        int n;  
        scanf("%d",&n);  
        long long b=0xfffffff;  
        for(int i=1;i<=100000;i++){  
            int tmp=i;  
            int summ=0;  
            while(tmp){  
                summ+=tmp%10;  
                tmp/=10;  
            }  
            a[summ].cishu++;  
            a[summ].sum+=i;  
            if(a[summ].cishu==n){  
                if(a[summ].sum<b)b=a[summ].sum;  
            }  
        }  
        cout<<b<<endl;  
    }  
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值