优快云周赛64期

优快云周赛64期

之前看到优快云周赛,就觉得很好,想把这个竞赛当成学习的机会,前面几期也报名了,但是因为时间的原因都没参加,过后还是看了题目,自己也尝试去做题和分析。
这次是第一次真正参赛

后面的两个编程题,第一题比较简单,很快就完成了,第二题一开始的想法是遍历所有的数,用链表保存乘积开立方不是整数的组合,但是之后如何在这些组合里求出最大的分组的时候就搞不定了,最后无奈放弃,然后写了一个用递归的方法,虽然知道肯定会超时,但是总会对几个用例的。

编号分组

题目说明

现欲对各有正整数编号的n个人进行分组,规定同组中不得出现两人编号乘积之开立方为正整数的情况,求构成最大分组的人数。

输入:单行n个数字表示各人的编号(0<n<100000,每个数字的范围在1到2000000000之间)

示例:
输入:27 4 2 16
输出:3

分析

假设输入的编号集合是x,求大分组个数是f(x)。求解过程:假设a是集合中的一个编号且是结果分组里的编号,集合中和a的乘积开方不是整数的编号组成了集合y,那么,f(x) = 1 + f(y),1表示a在最大分组里面。通过递归求a在最大分组时的人数,同理,遍历整个输入的编号,求b,c,d……n在最大分组时的人数,取最大值。

代码

注意:不要直接用下面的代码,代码有问题。不是思路的问题,而是C语言pow函数的问题,后面详细说明

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

#define IS_INT(x)   ((x) - (long long)(x) < 1e-7)


unsigned int max_goup_number(unsigned int numbers[], unsigned int n)
{
    unsigned int max = 0;

    if (n == 1)
        return 1;
    for (int i = 0; i < n; i++)
    {
        unsigned int group[n];
        int count = 0;  // 有多少个数和numbers[i]的乘积不是整数
        unsigned int group_max = 0;
        for (int j = 0; j < n; j++)
        {
            if (i == j)
                continue;
            double amass = (long long)numbers[i] * (long long)numbers[j];
            double extraction = pow(amass, 1.0/3.0);  // pow函数有问题
            // double extraction = cbrt(amass);
            if (IS_INT(extraction))
                continue;
            group[count++] = numbers[j];
        }
        // printf("%u ",numbers[i]);
        // for (int k = 0; k < count; k++)
        // {
        //     printf("%u ",group[k]);
        // }
        // printf("\n");
        group_max = 1 + max_goup_number(group,count);
        if (max < group_max)
        {
            max = group_max;
            break;
        }
        
    }
    return max;
}


int main()
{
    unsigned int numbers[100000]; // 编号
    int n = 0;            // 编号数量/人数
    char c;
    // 1. 读取数据
    do
    {
        scanf("%d",&numbers[n++]);
    } while ((c = getchar()) != '\n');
    // printf("n=%d\n",n);
    // for (int i = 0; i < n; i++)
    // {
    //     printf("%u ",numbers[i]);
    // }
    // printf("\n");
    printf("%d",max_goup_number(numbers,n));
}
坑爹的pow()函数

求一个数的开立方根,我第一个想到的是pow()函数,函数原型:double pow(double x, double y),是求x^y,把y设置成1.0/3就行了。

我发现只对了一个测试用例,我很郁闷,难道其他测试用例都超时了吗?结果出来之后,我看了大神写的总结,里面的提到了测试用例,我试了几个测试用例,结果是不对的,我检查了代码,逻辑没问题啊。

我首先想到的就是判断是否是整数的宏有问题,#define IS_INT(x) ((x) - (long long)(x) < 1e-7),专门测试了这个宏,没有问题,把double类型强转成整形会去掉小数点,然后取整的。我在代码加了log,确实是有些乘积开立方是整数但是被判断成了非整数。最后我发现是对pow()的结果强转成整形有问题。

int main()
{
    double x1 = pow(1, 1.0/3);
    double x2 = pow(8, 1.0/3);
    double x3 = pow(27, 1.0/3);
    double x4 = pow(64, 1.0/3);
    double x5 = pow(125, 1.0/3);
    double x6 = pow(216, 1.0/3);
    double x7 = pow(343, 1.0/3);

    printf("x1:%f,%ld\n",x1, (long long)x1);
    printf("x2:%f,%ld\n",x2, (long long)x2);
    printf("x3:%f,%ld\n",x3, (long long)x3);
    printf("x4:%f,%ld\n",x4, (long long)x4);
    printf("x5:%f,%ld\n",x5, (long long)x5);
    printf("x6:%f,%ld\n",x6, (long long)x6);
    printf("x7:%f,%ld\n",x7, (long long)x7);
}

结果如下:

x1:1.000000,1
x2:2.000000,2
x3:3.000000,3
x4:4.000000,3
x5:5.000000,4
x6:6.000000,5
x7:7.000000,6

x4,x5,x6,x7在强转之后小了1

如果直接赋值之后强转:

int main()
{
    double x1 = 1.000000000;
    double x2 = 2.000000000;
    double x3 = 3.000000000;
    double x4 = 4.000000000;
    double x5 = 5.000000000;
    double x6 = 6.000000000;
    double x7 = 7.000000000;

    printf("x1:%f,%ld\n",x1, (long long)x1);
    printf("x2:%f,%ld\n",x2, (long long)x2);
    printf("x3:%f,%ld\n",x3, (long long)x3);
    printf("x4:%f,%ld\n",x4, (long long)x4);
    printf("x5:%f,%ld\n",x5, (long long)x5);
    printf("x6:%f,%ld\n",x6, (long long)x6);
    printf("x7:%f,%ld\n",x7, (long long)x7);
}

结果完全没问题:

x1:1.000000,1
x2:2.000000,2
x3:3.000000,3
x4:4.000000,4
x5:5.000000,5
x6:6.000000,6
x7:7.000000,7

为什么pow()会有问题呢?我也没搞懂,有知道的评论区跟我说一下。

C语言求立方根有一个专门的函数double cbrt(double x),用这个函数就没问题。用这个函数替换上面的pow,测试用例的结果就没问题了。

小球游戏

题目说明

某台有10个小球的游戏机,其设定的规则如下:
每一轮游戏在开始之前会把编号为0到9的小球依次放入从左到右编号也为0到9的10个位置;游戏开始后会快速对调任意两个球的位置若干次,并在结束时要求观众写出从左到右的小球编号顺序,写对就得奖。
由于速度很快,所以直接靠观看写对很难。但有个程序员发现这台游戏机其实有一个固定的长度为n的操作序列数据库,每一轮游戏都是随机取一个起始操作序列编号和一个结束操作序列编号(操作序列编号从1到n)并从起始到结束依次执行每个操作序列编号对应的操作,而每个操作序列编号对应的操作就是对该次操作指定的两个编号的位置上的小球进行对调。
现在给出操作序列数据库和每一轮游戏的起始操作序列编号和结束操作序列编号,求每轮游戏结束时从左到右的小球编号顺序。

分析

这个没什么好说的

代码

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void swap(int *a, int *b)
{
    int tmp = *a;
    *a = *b;
    *b = tmp;
}
int main()
{
    // int seq[10] = {0,1,2,3,4,5,6,7,8,9};
    int n,m;
    unsigned int *operator1, *operator2;
    unsigned int *start, *end;
    int i;
    scanf("%d %d",&n,&m);
    // int operator1[n], operator2[n];
    // int start[m], end[m];
    operator1 = (unsigned int *)malloc(sizeof(int) * n);
    if (NULL == operator1)
    {
        printf("operator1 is NULL");
    }
    operator2 = (unsigned int *)malloc(sizeof(int) * n);
    if (NULL == operator2)
    {
        printf("operator1 is NULL");
    }
    start = (unsigned int *)malloc(sizeof(int) * m);
    end = (unsigned int *)malloc(sizeof(int) * m);
    for (i = 0; i < n; i++)
    {
        scanf("%d %d",operator1+i,operator2+i);
    }
    for (i = 0; i < m; i++)
    {
        scanf("%d %d",start+i,end+i);
    }
    for (i = 0; i < m; i++)
    {
        unsigned int c = *(start + i);
        unsigned int d = *(end + i);
        int seq[10] = {0,1,2,3,4,5,6,7,8,9};
        for (int j = c; j <= d; j++)
        {
            int index1 = operator1[j-1];
            int index2 = operator2[j-1];
            swap(&seq[index1],&seq[index2]);
        }
        for (int k = 0; k < 9; k++)
        {
            printf("%d ",seq[k]);
        }
        printf("%d\n",seq[9]);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值