240510.LeetCode——1769.移动所有球到每个盒子所需的最小操作数

题目描述

有 n 个盒子。给你一个长度为 n 的二进制字符串 boxes ,其中 boxes[i] 的值为 '0' 表示第 i 个盒子是  的,而 boxes[i] 的值为 '1' 表示盒子里有 一个 小球。

在一步操作中,你可以将 一个 小球从某个盒子移动到一个与之相邻的盒子中。第 i 个盒子和第 j 个盒子相邻需满足 abs(i - j) == 1 。注意,操作执行后,某些盒子中可能会存在不止一个小球。

返回一个长度为 n 的数组 answer ,其中 answer[i] 是将所有小球移动到第 i 个盒子所需的 最小 操作数。

每个 answer[i] 都需要根据盒子的 初始状态 进行计算。

EX1.

7773632424c94c818732d1daf7fc7e7b.jpg 

EX2.

3ec52eac09504f119b00038ef797c6bf.jpg

提示

  • n == boxes.length
  • 1 <= n <= 2000
  • boxes[i] 为 '0' 或 '1'

 代码实现

C语言

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */

//该函数返回的数组必须是内存动态分配定义的,假设在函数调用后,调用者会对该数组进行内存清理
int* minOperations(char* boxes, int* returnSize)
{
    int i;
    int j;//定义循环变量
    int sum=0;//中间变量
    int len;
    len=strlen(boxes);//利用字符串函数strlen获得字符串boxes的长度
    int* answer=(int*)malloc(sizeof(int)*len);//动态规划定义与boxes等长的返回数组
    for(i=0;i<len;i++)//第一层循环遍历选定一个箱子
    {

        //原本的sum=0;在这,但为了程序可读性,移动到循环末尾,并在定义处进行初始化
        for(j=0;j<len;j++)//当前箱子为基准,计算其他箱子的球调度到该箱子所需操作步数
        {
            if(i==j)//跳过计算当前箱子到当前箱子的调度过程
            {
                continue;//continue是跳过当次循环,执行下次,break是跳过当层循环,执行下一语句,这里只需跳过一次循环,而不是一重循环(有点绕...)
            }
            if(boxes[j]=='1')//这一判断语句要有,这一步卡了很久,待会细说
            {
                sum=sum+abs(i-j);//计算每个箱子到基准箱子所需的操作数,并加到总和
            }
        }
        answer[i]=sum;//赋总和给answer的指定下标的元素

        sum=0;//然后清空中间变量
    }
    *returnSize=len;//设置返回数组的大小,也就是boxes的大小
    return answer;
}

思路

       拿到题目,粗略读了一遍,大概过了一遍,脑子里大概想到了要用循环,具体几层?每层循环要干嘛?还不懂。细想了一下,感觉有点麻烦,要以每个箱子为对象,计算每个箱子到指定箱子需要的步数?感觉有点难实现。然后在纸质草稿纸上推演了一下,在循环里只需要计算每个箱子到指定箱子的下标之差(绝对值)就可以,然后再加一层循环,遍历每个指定箱子,大体思路就是这样,开动!大功告成,运行!报错!.......

       原因错在哪?我貌似忽略了一点...boxes貌似是个字符串!不是int,如果需要直接用boxes的值来做运算,那你需要转换。这里我尝试了几个方法,一起来看看吧。

第一种:

       (int)boxes[i];//编译成功,结果错误

       原因是这样转换,‘1’得到的并不是1,而是‘1’的ASCII码,最后得出来数值偏大。

第二种:

       int num=boxes[j]-'0';
       sum=sum+num*abs(i-j);//编译成功,结果正确

       这是百度搜出来的,学到的,如果不想用函数转换,也可以自己设计一个语法计算出来数值,当然如果可以封装成函数就更好了。(个人认为封装成函数泛化能力不强,只对0~9有效)

第三种:

       boxes_copy=atoi(boxes);//不可行,下面分析

       也是百度搜出来的,函数atoi()就是把字符串转换成一个整型数字,原理就是用一个新的整型变量来把boxes变成一个数,我一开始认为是可行的,原因是可以用这个整型数字,每次取一个固定位数,然后作为箱子中的球的有无,用循环可以实现,也能封装成函数,虽然麻烦,而且代价过高,但可行。

      后面细想了一下还是有不少困难的,首先如何实现取指定位数?取第一位,最后一位固然方便,但如果是取中间的呢?还是只能用循环来敲定?个人认为还是比较困难,第二个是这个函数是把一个字符串转变成整型数,但如果这个字符串是00001呢?转换成整型数是1,那001?也是1?那转换前后不就有信息损失了嘛,所以综上归根到底还是不可行。

       解决了上面困难这道题基本就结束了,然后提交就行,下面是反馈结果。

b747572e4e90493fa07d081ef29a2cee.jpg

50+60,还行,但还能优化吗?下面看看题解区大佬的做法

C语言【题解区大佬】

/**
 * Note: The returned array must be malloced, assume caller calls free().
 */
#include<math.h>
#include<stdlib.h>
int* minOperations(char* boxes, int* returnSize) {
    int a = strlen(boxes);
    int* res = (int*)malloc(sizeof(int) * a);
    int left = 0, right, num = 0;
    int i;
    for (i = 0;i < a;i++) {
        res[i] = 0;
    }
    for (i = 0;i < a;i++) {
        if (boxes[i] == '1') {
            res[0] += i;
            num++;
        }
    }
    right = num;
    for (i = 1;i < a;i++) {
        if (boxes[i - 1] == '1') {
            left++;
            right--;
        }
        res[i] = res[i - 1] - right + left;

    }
    *returnSize = a;
    return res;
}

//只能说根本看不懂...大佬的逻辑太强大了...

喂给AI喂了三次才勉强理解,大概就是

       对于`boxes`中的每个1,我们更新左右边界(`left`和`right`)。`left`表示1左边的0的数量,`right`表示1右边的0的数量。对于每个位置,我们计算操作数为:前一个位置的操作数减去`right`(因为这些0不需要移动),再加上`left`(因为这些0需要移动到前面)。

      还是太难懂了...先贴在这,以后大成了再回来看吧...顺便观摩一下大佬的80+95...

30220ef94ccf4f40bb1d6e274bf75efa.jpg

 总结

      还是拖到现在才收尾,8:51,刷力扣到底有什么收获呢?自学的困难要记住。今天看CSDIY的时候看到一句话很有感触。

       自学的一大困难就是难以自律,你在自学算法的时候你会问自己,到底收获了什么?到底有什么用?看着身边人都在玩游戏,或者学数学,学英语,这一整个过程没有学分,没有绩点,没有老师,没有同学...但心中请秉持着这样一个信念:你在变强

       还是回到题目本身,这道题学到的大概是一些思维的转换,有些细节末枝,关于数据类型的转换,希望以后有帮助。(明天要补课...)

      顺便扯一些有的没的吧,还是希望自己能加强时间密度,放松的时候好好放松,卷的时候好好卷,期中考试成绩也出来了,在意料之中吧,但是看到一些上课从来不听的人居然比我高了差不多15分的时候还是很惊讶的...但....谁知道呢,怎么知道人家每天在宿舍不是在学习呢?当然,乾坤未定,你我皆是黑马。有一群喜欢学习,追求卓越的朋友也蛮好的。明天继续加油!

  • 30
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值