找出3的最大倍数的整数集合

题目描述:给一个包含非负整数的数组(长度为n),找出由这些数字组成的最大的3的倍数,没有的话则输出impossible。例如,如果输入的数组为{8,1,9},输出应为“9 8 1”,并且如果输入的数组为{8,1,7,6,0},输出应为”8760″。

方法一 :暴力
直接用蛮力的话,生成所有的组合,为 2n 个,对每个数字再进行比较判断,需要 O(n) 的时间,因为n可能会比较大,需要每个位的比较。
总的时间复杂度为 O(n2n) .

:
可以借助O(n)的额外空间有效的解决这个问题。该方法是基于对数以下的简单性质:

  • 1)一个数是3的倍数,则该数字各位总和为3的倍数。例如,让我们考虑8760,它是3的倍数,因为数字总和为8 + 7 + 6 + 0 = 21,这是3的倍数。
  • 2)如果一个数是3的倍数,那么它的所有排列也为3的倍数,例如,6078是3的倍数,数字8760,7608,7068,…也为3的倍数。
  • 3)一个数的所有位之和与该数会有相同的余数。例如,如果对于151和它的各位之和7,对于3的余数都为1。

我们可以用下面的算法:

  • 1.对数组进行非递减排序。
  • 2.用3个队列 queue0,queue1,queue2,分别存储除以3余数为 0、1、2的数字。
  • 3.求得所有的为的总和sum
  • 4.有下面三种情况:
    • a) sum除以3余0。出列的所有三个队列中的数字,以非递减顺序排序输出到结果数组中。
    • b) sum除以3余1。则尝试从queue1中移除一个元素或从queue2中移除两个元素,如果不可以的话,则说明impossible
    • c) sum除以3余2。则尝试从queue1中移除两个元素或从queue2中移除一个元素,如果不可以的话,则说明impossible
  • 5.最后将3个队列中的所有元素都输出到结果数组中,非递减排序,即为最终结果。

下面是C语言实现代码:

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

// 自定义队列节点
typedef struct Queue
{
    int front;
    int rear;
    int capacity;
    int* array;
} Queue;

//创建一个队列
Queue* createQueue( int capacity )
{
    Queue* queue = (Queue *) malloc (sizeof(Queue));
    queue->capacity = capacity;
    queue->front = queue->rear = -1;
    queue->array = (int *) malloc (queue->capacity * sizeof(int));
    return queue;
}

// 检测队列是否为空
int isEmpty (Queue* queue)
{
    return queue->front == -1;
}

//想队列添加一个元素
void Enqueue (Queue* queue, int item)
{
    queue->array[ ++queue->rear ] = item;
    if ( isEmpty(queue) )
        ++queue->front;
}

// 从队列删除一个元素
int Dequeue (Queue* queue)
{
    int item = queue->array[ queue->front ];
    if( queue->front == queue->rear )
        queue->front = queue->rear = -1;
    else
        queue->front++;

    return item;
}

// 打印数组
void printArr (int* arr, int size)
{
    int i;
    for (i = 0; i< size; ++i)
        printf ("%d ", arr[i]);
}

int compareAsc( const void* a, const void* b )
{
    return *(int*)a > *(int*)b;
}
int compareDesc( const void* a, const void* b )
{
    return *(int*)a < *(int*)b;
}

// 将3个队列中的元素输出到辅助数组
void populateAux (int* aux, Queue* queue0, Queue* queue1,
                            Queue* queue2, int* top )
{
    while ( !isEmpty(queue0) )
        aux[ (*top)++ ] = Dequeue( queue0 );

    while ( !isEmpty(queue1) )
        aux[ (*top)++ ] = Dequeue( queue1 );

    while ( !isEmpty(queue2) )
        aux[ (*top)++ ] = Dequeue( queue2 );
}

int findMaxMultupleOf3( int* arr, int size )
{
    // 第1步,排序
    qsort( arr, size, sizeof( int ), compareAsc );

    Queue* queue0 = createQueue( size );
    Queue* queue1 = createQueue( size );
    Queue* queue2 = createQueue( size );

    // 第2,3步
    int i, sum;
    for ( i = 0, sum = 0; i < size; ++i )
    {
        sum += arr[i];
        if ( (arr[i] % 3) == 0 )
            Enqueue( queue0, arr[i] );
        else if ( (arr[i] % 3) == 1 )
            Enqueue( queue1, arr[i] );
        else
            Enqueue( queue2, arr[i] );
    }

    //第四部,b)
    if ( (sum % 3) == 1 )
    {
        if ( !isEmpty( queue1 ) )
            Dequeue( queue1 );
        else
        {
            if ( !isEmpty( queue2 ) )
                Dequeue( queue2 );
            else
                return 0;
            if ( !isEmpty( queue2 ) )
                Dequeue( queue2 );
            else
                return 0;
        }
    }

    // 第4步,c)
    else if ((sum % 3) == 2)
    {
        if ( !isEmpty( queue2 ) )
            Dequeue( queue2 );
        else
        {
            if ( !isEmpty( queue1 ) )
                Dequeue( queue1 );
            else
                return 0;
            if ( !isEmpty( queue1 ) )
                Dequeue( queue1 );
            else
                return 0;
        }
    }

    int aux[size], top = 0;

    // 第5步
    populateAux (aux, queue0, queue1, queue2, &top);
    qsort (aux, top, sizeof( int ), compareDesc);

    // 打印结果
    printArr (aux, top);

    return 1;
}

// 测试
int main()
{
    int arr[] = {8, 1, 7, 6, 0};
    int size = sizeof(arr)/sizeof(arr[0]);

    if (findMaxMultupleOf3( arr, size ) == 0)
        printf( "Not Possible" );

    return 0;
}

关于 程序算法艺术与实践更多讨论与交流,敬请关注本博客和新浪微博songzi_tea.

### 回答1: 要计算任意正整数n以内中三或五的倍数的自然之和,可以使用学公式。首先,计算出n以内三的倍数的自然之和,即: 3 + 6 + 9 + ... + 3k 其中k为小于等于n的最大的三的倍数,即k = ⌊n/3⌋。这个等差列的公差为3,首项为3,末项为3k,因此可以使用等差和公式计算出它的和: S1 = [k/2] × [3 + 3k] 接下来,计算出n以内五的倍数的自然之和,即: 5 + 10 + 15 + ... + 5m 其中m为小于等于n的最大的五的倍数,即m = ⌊n/5⌋。这个等差列的公差为5,首项为5,末项为5m,因此可以使用等差和公式计算出它的和: S2 = [m/2] × [5 + 5m] 但是,上面的计算中有一些问题。如果直接按照上面的公式计算,会把既是三的倍数又是五的倍数字(例如15)计算两次,因此需要减去这些字的重复部分。这些字就是所有的15的倍数,它们的和为: 15 + 30 + 45 + ... + 15n 其中n为小于等于n的最大的15的倍数,即n = ⌊n/15⌋。这个等差列的公差为15,首项为15,末项为15n,因此可以使用等差和公式计算出它的和: S3 = [n/2] × [15 + 15n] 所以,n以内中三或五的倍数的自然之和为: S = S1 + S2 - S3 将上面的公式代入,可以得到: S = [⌊n/3⌋/2] × [3 + 3⌊n/3⌋] + [⌊n/5⌋/2] × [5 + 5⌊n/5⌋] - [⌊n/15⌋/2] × [15 + 15⌊n/15⌋] 例如,如果要计算100以内中三或五的倍数的自然之和,可以将n取为100,代入上面的公式得到: S = [⌊100/3⌋/2] × [3 + 3⌊100/3⌋] + [⌊100/5⌋/2] × [5 + 5⌊100/5⌋] - [⌊100/15⌋/2] × [15 + 15⌊100/15⌋] = 1683 因此,100以内中三或五的倍数的自然之和为1683。 ### 回答2: 我们可以使用学的方法来计算得出任意正整数n以内中三或五的倍数的自然之和。 首先,我们可以找出n以内所有的三的倍数和五的倍数。分别计算出三的倍数之和和五的倍数之和,并且注意到如果某个同时是三和五的倍数,则在计算过程中会重复计算两次。因此,我们需要减去重复计算的部分。 设n以内的三的倍数之和为sum_three,五的倍数之和为sum_five,同时是三和五的倍数之和为sum_fifteen。 计算三的倍数之和:sum_three = 3 + 6 + 9 + ... + m*3,其中m为不大于n/3的最大整数。由于等差列的和公式为Sn = (a1 + an) * n / 2,其中a1为首项,an为末项,n为项。我们可以将sum_three = 3 * (1 + 2 + 3 + ... + m),利用等差列的和公式得到sum_three = 3 * m * (m + 1) / 2。 计算五的倍数之和:sum_five = 5 + 10 + 15 + ... + k*5,其中k为不大于n/5的最大整数。同样利用等差列的和公式,得到sum_five = 5 * k * (k + 1) / 2。 计算重复部分之和:sum_fifteen = 15 + 30 + 45 + ... + p*15,其中p为不大于n/15的最大整数。同样利用等差列的和公式,得到sum_fifteen = 15 * p * (p + 1) / 2。 最后,我们可以得到n以内中三或五的倍数的自然之和为sum = sum_three + sum_five - sum_fifteen。 那么进行具体计算,sum = 3 * m * (m + 1) / 2 + 5 * k * (k + 1) / 2 - 15 * p * (p + 1) / 2。我们可以遍历m、k和p,找出不大于n/3、n/5和n/15的最大整数,然后计算出sum的值。 综上所述,我们可以根据以上步骤计算出任意正整数n以内中三或五的倍数的自然之和。 ### 回答3: 要计算任意正整数n以内中三或五的倍数的自然之和,我们可以使用和公式和排除法。 首先,我们将n以内的自然从1到n分别除以3和5,并将能整除的记录下来。记为集合A和集合B。其中,集合A表示n以内的自然数中的三的倍数集合B表示n以内的自然数中的五的倍数。 接下来,我们将集合A和集合B中的字分别相加,得到它们各自的和,分别记为sumA和sumB。 然后,我们需要注意的是,集合A和集合B中可能有一些字同时是三的倍数和五的倍数,这样的字在和时被重复计算了。为了避免重复计算,我们需要将集合A和集合B中的重复字剔除。 假设集合A和集合B中的重复集合集合C,我们将集合C中的字分别从sumA和sumB中减去。 最后,我们将sumA和sumB相加得到最终的和,记为sum。 综上所述,我们可以得到计算任意正整数n以内中三或五的倍数的自然之和的公式: sum = sumA + sumB - sumC 在本题中,要计算十以内自然数中三或五的倍数的自然之和,我们有: n = 10 集合A = {3, 6, 9} 集合B = {5} 集合C = {} sumA = 3 + 6 + 9 = 18 sumB = 5 sumC = 0 代入公式,我们有: sum = 18 + 5 - 0 = 23 所以,十以内自然数中三或五的倍数的自然之和为23。 以上是计算任意正整数n以内中三或五的倍数的自然之和的方法和解答。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值