代码随想录训练营25day-贪心算法3

本文讨论了如何利用贪心算法解决数组取反最大化问题,涉及1005k次操作,以及134号加油站和135号分发糖果的问题,通过对比暴力解法和贪心方法优化解决方案。
摘要由CSDN通过智能技术生成

一、1005 k次取反后最大化数组

主要是用贪心的思维解决问题,达到训练的目的。题目中说明了必须要用k次,数组也有负数或者正数,怎么让数组最大化呢?

1 k次范围内,把所有的负数全部翻转,这样能够最大化;

2 如果还剩下次数k-n(n是负数的个数),那么怎么最大化呢?一开始思考时候,把最小的数字依次翻转,代价最小,但是这个思路是错误的,找到现在数组最小的元素,来回翻转(题目说同一个元素可以来回翻转),损失最多就是减少该值,这样代价才最小。

3 综合上面两个情况,这样能全局最优,获取最大的值。

void sortArr(int* nums, int numsSize)
{
    for(int i = 0; i < numsSize; i++)
    {
        for(int j = i + 1; j < numsSize;j++)
        {
            if(nums[i] > nums[j])
            {
                int tmp = nums[i];
                nums[i] = nums[j];
                nums[j] = tmp;
            }
        }
    }
}
int largestSumAfterKNegations(int* nums, int numsSize, int k) {
    int sum = 0;
    int pos = 0;
    if(numsSize <= 0)
    {
        return 0;
    }
    sortArr(nums, numsSize);
    for(int i = 0; i < numsSize; i++)
    {
        if(k > 0 && nums[i] <= 0)
        {
            nums[i] *= -1;
            k--;
            //pos++;
        }
    }
    printf("k = %d\n", k);
    sortArr(nums, numsSize);
    if(k % 2 == 1)//--为+  -+为-
    {
       nums[pos] *= -1;//奇数次翻转为该数取反
    }

    for(int i = 0; i < numsSize; i++)
    {
        printf("%d-num = %d\n", i, nums[i]);
        sum += nums[i];
    }
    return sum;
}

二、134 加油站

方法一 暴力解法(会超时)

主要是遍历cost数组,从i=0开始,如果油量有剩余,且能回到原点,那么存在这样一个位置,可以满足条件。

int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize) {
    //暴力法
    int index = 0;
    for(int i = 0; i < costSize; i++)
    {
        int rest = gas[i] - cost[i];
        index = (i + 1) % costSize;
        //循环一圈
        while(rest > 0 && index != i)
        {
            rest  += gas[index] - cost[index];
            index = (index + 1) % costSize;
        }

        if(rest >= 0 && index == i)
        {
            return i;
        }
    }
    return -1;
}

 方法二 贪心算法

 从index  = 0开始计算油量差,累加到cursums,如果小于0,无论怎样,走到i位置都会断油,从下一个位置接着重新计算。

int canCompleteCircuit(int* gas, int gasSize, int* cost, int costSize) {


    int pos = 0;
    int cursums = 0;
    int total = 0;
    for(int i = 0; i < gasSize; i++)
    {
        int reset = gas[i] - cost[i];

        cursums += reset;
        total  += reset;
        if(cursums < 0)
        {
            pos  = i + 1;
            cursums = 0;
        }
    }
    if(total < 0) 
      return  -1; //总的剩余油量为负数,没有能走完一圈的点

    return  pos;
}

三、135 分发糖果

 1 初始化数组(size是ratingsize),用来保存糖果个数;

 2 需要考虑左和右两边,可以拆开来看:

     先看左边,从左往右遍历,如果当前位置的孩子分数大于左边,那么在左边糖果基础上+1;

    然后看右边,如果当前位置的孩子大于右边孩子,这里需要考虑左边情况,需要找到右边糖果加1(nums [i +1] + 1)和nums[i]的最大值,这样才能满足左右两边情况。

  3 最后结果全部相加。


int candy(int* ratings, int ratingsSize) {
    int result = 0;
    int* nums = (int*)calloc(ratingsSize, sizeof(int));
    //初始化为1,至少1个
    for(int i = 0; i < ratingsSize; i++)
    {
        nums[i] = 1;
    }
    for(int i = 1; i < ratingsSize; i++)
    {
        //左边和右边比较
        if(ratings[i] > ratings[i - 1])
        {
            nums[i] = nums[i - 1] + 1; 
        }
    }
    for(int i = ratingsSize - 2; i >= 0; i--)
    {
        if(ratings[i] > ratings[i + 1])
        {
            nums[i] = nums[i] > nums[i + 1] + 1? nums[i]: nums[i + 1] + 1;
        }
    }

    for(int i = 0; i < ratingsSize; i++)
    {
        result += nums[i];
    }

    return result;
}

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值