洛谷普及B3621 枚举元组

题目:枚举元组

题号:B3621

难度:普及一


题目分析

第一个参数n决定输出每一行的个数,

第二个参数k决定输出数字的最大值。

并且得到的枚举元组按照字典序递增排序分行输出。

基础思路

关于这题我的思路并不是真的枚举出每个符合条件的数字来组成元组,

既然是按照字典序来逐一排列,那么每一行按顺序组成一个整数(n位大小的数)

然后从小到大按照数值大小排序就可以,

例如图中的(元组)

(1 ,1)(1,2)(1,3)

(2,1)(2,2)(2,3)

(3,1)(3,2)(3,3)

我们直接先当成(合并数值)

(11)(12)(13)

(21)(22)(23)

(31)(32)(33)

来排序,

最后再分离单个数字每组换行输出即可,但关键问题还没解决,怎么得到符合条件的这些数,

如果需要直接枚举得到元组合数(一个元组组合成一个整数的数),我们又需要一个怎样的范围。

思路实现

既然要枚举,就要有不断被精确的范围,关于本题,

n位数,且每个数组在1~k

那么很显然,精确范围在  n个1 ~ n个k

例如 n = 3,k = 4,那么 min = 111,max = 444

则具体枚举范围就在 min~max中,首先n位数肯定满足了。

然后再判断其中符合条件的有效数值,需满足条件:数值中每个数字<=k

熟练利用模块化将复杂问题分解为多个简单的子问题

那么我们需要写的程序模块(或函数)便有:

模块1:通过传入的参数n和k计算出对应的min和max,控制枚举范围。

模块2:参数传入枚举出的数值,分离出数值的每位数字,保证均不大于k

模块3:将枚举出并且通过判断有效的数值存储在数组中进行递增排序。

模块4:将判断和排序处理好的整数分离出每位以空格隔开输出,每组各占一行。

开始上代码。

代码实现

模块一(得枚举范围min和max)

int left = 0;
int right = 0;
for(int i=1;i<=n;i++)
{
    left = left*10 + 1;
    right = right*10 + k;
}

模块二(判断每位数不大于k)

int pd(int n,int k) { //检测n中数字都小于等于k
   int a;
    while (n > 0) {
        a = n % 10;
        if (a > k||a==0)
            return 0; 
        n /= 10;
    }
return 1;
}

模块三(储存排序)

for(int i=left;i<=right;i++)
{
if(pd(i,k))
{   count++;
    a[count]=i; }
}
for(int i=1;i<=count-1;i++)
for(int j=i;j<=count-i;j++)
if(a[j]>a[j+1])
{    int t=a[j];
    a[j]=a[j+1];
    a[j+1]=t;    }

模块四(分离输出)

//函数
void ps(int n) { //分离输出n
    int a[10],num=0;
    while (n > 0) {
        a[++num] = n % 10;
             n /= 10;
    }
for(int i=num;i>=1;i--){
printf("%d",a[i]);//倒序输出,因为是从低位到高位分离的,所以输出的时候需要倒序输出,才能得到正确的数before
if(i!=1)
printf(" ");//注意不要给末尾留无用空格,有的题比较严格
}
}


//插入主函数代码块
for(int i=1;i<=count;i++)
{
ps(a[i]);
printf("\n");
}

代码总和

#include <stdio.h>
#include <math.h>

int pd(int n,int k) { //检测n中数字都小于等于k
   int a;
    while (n > 0) {
        a = n % 10;
        if (a > k||a==0)
            return 0; 
        n /= 10;
    }
return 1;
}
void ps(int n) { //分离输出n
    int a[10],num=0;
    while (n > 0) {
        a[++num] = n % 10;
             n /= 10;
    }
for(int i=num;i>=1;i--){
printf("%d",a[i]);//倒序输出,因为是从低位到高位分离的,所以输出的时候需要倒序输出,才能得到正确的数before
if(i!=1)
printf(" ");
}
}

int main() {
    int n,k,a[100000],count=0;
     scanf("%d %d", &n, &k);
int left = 0;
int right = 0;
for(int i=1;i<=n;i++)
{
    left = left*10 + 1;
    right = right*10 + k;
}
for(int i=left;i<=right;i++)
{
if(pd(i,k))
{   count++;
    a[count]=i; }
}
for(int i=1;i<=count-1;i++)
for(int j=i;j<=count-i;j++)
if(a[j]>a[j+1])
{int t=a[j];a[j]=a[j+1];a[j+1]=t;}
for(int i=1;i<=count;i++)
{
ps(a[i]);
printf("\n");
}
    return 0;
}

总结

同样是枚举,我觉得可以温柔一点的枚举,不要太暴力!

题目评论区下面都是暴力枚举哈,有点意思,

我觉得能优化的地方最好优化一下,(纯素养 非 强迫症)

以上就是本人对于该题的全部思路,

该题模块化解决子问题时都是挺简单的模块,整体难度一般。

我测试了,该代码的测试上限在n=6,k=6;满足本题n=5;k=4;

看似很接近,实际上差的还很远

生成的元组内数字数量和满足count = n * k^n ;排列

count(6,6) = 6 * 6^6 =279936 ≈ 28万.

count(5,4) = 5 * 4*5 = 5120 ≈ 5千.

28万 远大于 5千 的算力极限   。所以优化算法还是很有必要(养成优化习惯)。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LAOLONG-C

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值