zoukankan      html  css  js  c++  java
  • 力扣-第52场双周赛

    四个题目难度分别为简单、中等、中等、困难;解法不一定最优,能AC,欢迎交流。

    5742.将句子排序

    题意:有一个类似“is2 sentence4 This1 a3”,长度不超过200的字符串,单词后面跟着数字,单词用空格隔开,要求按数字升序重新拼接字符串并去掉数字,变成“This is a sentence”。

    思路:单词数不超过9,只需要1位来表示单词应该存在的位置;按空格分割字符串,提取数字放到新单词数组的指定位置,最后拼接时去掉数字。

        /** 5742.将句子排序
         * 按空格分割字符串,提取数字放到新单词数组的指定位置,最后拼接时去掉数字。
         */
        public String sortSentence(String s) {
            //分割
            String[] a=s.split(" ");
            int n=a.length;
            String[] ans=new String[n];
            //每个字符串的最后一个是数字,落到新字符串中
            for(int i=0;i<n;i++){
                int idx=a[i].charAt(a[i].length()-1)-'0';
                ans[idx-1]=a[i];
            }
            //拼接排序后的字符串,注意中间的空格数比单词数少1
            String res="";
            for(int i=0;i<n;i++){
                if(i==0)
                    res=res+ans[i].substring(0,ans[i].length()-1);
                else
                    res=res+" "+ans[i].substring(0,ans[i].length()-1);
            }
            return res;
        }

     5743. 增长的内存泄露

    题意:有两个内存条,剩余内存分别为m1和m2;现有一个程序在消耗内存,第i秒消耗i内存;每秒钟,如果两个内存条剩余内存大者被程序消耗,相同则消耗第一个内存条;两个内存条的剩余内存都不满足程序消耗则程序奔溃;问程序何时奔溃;

    思路:模拟程序运行的消耗,按条件减少内存条的剩余内存,不满足条件时返回;

        /** 5743. 增长的内存泄露
         * 模拟程序运行的消耗,按条件减少内存条的剩余内存,不满足条件时返回;
         */
        public static int[] memLeak(int m1, int m2) {
            int[] res=new int[3];
            int t=1;
            //需要占用的内存比两个内存条都大,则程序奔溃
            while(!(t>m1 && t>m2)){
                if(m1>=m2)
                    m1-=t;
                else
                    m2-=t;
                t++;
            }
            res[0]=t;
            res[1]=m1;
            res[2]=m2;
            return res;
        }

     5744. 旋转盒子

    题意:现有m*n的字符矩阵,字符代码石头、固定物、空位置;将矩阵顺时针旋转90°,石头如果悬空会因为惯性落下;求最后的字符矩阵;

    思路:

    1.不管三七二十一,先顺时针旋转90°;

    2.对于每一列,从下往上遍历,找到两个固定物之间的信息,固定物包含地底和天花板;

    3.计算上下固定物之间的石头数,从固定物下方堆砌石头,剩余的空间用空位置填充;

    4.注意点:地底和天花板作为固定物的表示

    时空复杂度都为(n*m)

        /** 5744. 旋转盒子
         *
         */
        public char[][] rotateTheBox(char[][] box) {
            int n=box.length,m=box[0].length;
            char[][] res=new char[m][n];
            //正常旋转90
            for(int i=0;i<n;i++){
                for(int j=0;j<m;j++)
                    res[j][n-1-i]=box[i][j];
            }
            n=res.length;
            m=res[0].length;
            //石头落下去,每一列从下往上,分为4种情况[地底,障碍物][地底,天上][障碍物,障碍物][障碍物,天上]
            for(int j=0;j<m;j++){
                //up表示上方固定物,down表示下方固定物,初始化为天花板和地底,num表示(up,down)之间的石头数
                int up=-1,down=n,num=0;
                for(int i=n-1;i>=0;i--){
                    if(res[i][j]=='#'){//石头
                        num++;
                    }else if(res[i][j]=='*'){//固定物
                        if(num==0){
                            //由于是从下往上,所以固定物先确定下方
                            down=i;
                        }else {
                            //上下固定物区间确定了,开始石头掉落的过程模拟
                            up=i;
                            change(res,up,down,num,j);
                            //清空石头数,并更新下方固定物位置
                            num=0;
                            down=i;
                        }
                    }
                }
                //上方固定物为天花板
                if(num>0){
                    up=-1;
                    change(res,up,down,num,j);
                }
            }
            return res;
        }
        //j列中(up,down)之间有num块石头,从下往上堆,剩余空间用空位置填充
        public static void change(char[][] res,int up,int down,int num,int j){
            for(int i=down-1;i>up;i--){
                if(num>0){
                    res[i][j]='#';
                    num--;
                }else{
                    res[i][j]='.';
                }
            }
        }

     5212. 向下取整数对和

    题意:有长度为100000的数组,返回对所有下标0<=i,j<n的floor(a[i]/a[j])结果之和,答案对1e9+7求模;floor()函数返回整数部分;

    样例:

    输入[2,5,9] 输出10

    floor(2 / 5) = floor(2 / 9) = floor(5 / 9) = 0 floor(2 / 2) = floor(5 / 5) = floor(9 / 9) = 1 floor(5 / 2) = 2 floor(9 / 2) = 4 floor(9 / 5) = 1

    (0+1+2+4+1)%(1000000007)=10;

    思路:

    1.确定方向

    对于floor(x/y)函数,假设y一定,则有多个x使得floor(x/y)函数值相同;例如y=9,x∈[9,17]时函数值都是1,x∈[18,26]时函数值都是2, x∈[27,35]时函数值都是3;显然需要进行区间计数,线段树、树状数组、前缀和是常规操作,这里选择前缀和即可;

    2.看数据范围

    数组长度和元素大小都是105,可以直接开大数组num;

    先计数:num[i]表示元素i的个数;

    后变成前缀和数组:num[i]表示[0,i]的元素个数总和,通过num[i]-num[i-1]则表示元素i的个数;

    这里最大元素maxx作为数组长度,不需要每次都开满数组长度为105,节省空间;

    3.倍数关系

    对于元素i,每次找一段区间内的元素总个数,计算函数值之和;

    倍数区间形如[i,i×2-1]、[i×2,i×3-1]、[i×3,i×4-1] ... [i×(j-1),i×j-1];

    倍数×区间内的元素总个数 = 元素i在该段区间的函数值总和;

    元素i的个数×倍数×区间内的元素总个数 = 所有i在该段区间的函数值总和;

    再对多段区间进行累加即可;

    4.越界情况

    极限数据105×105=1010会导致int溢出,计算过程用long变量,最后转int;

    当i*j>maxx时直接使用i*j作为数组下标会使得数组越界;

        /** 5212. 向下取整数对和
         * 求所有floor(a[i] / a[j])之和
         * 前缀和
         */
        public int sumOfFlooredPairs(int[] a) {
            long res=0,p=1000000007;
            int n=a.length,maxx=0;
            for(int i=0;i<n;i++){
                maxx=Math.max(maxx,a[i]);
            }
            int[] num=new int[maxx+1];
            //计数
            for(int i=0;i<n;i++)
                num[ a[i] ]++;
            //前缀和
            for(int i=1;i<=maxx;i++)
                num[i]+=num[i-1];
            for(int i=1;i<=maxx;i++){
                //x表示数字i的个数
                long x=num[i]-num[i-1];
                if(x==0)
                    continue;
                //找区间,[i,i*2-1]、[i*2,i*3-1]、[i*3,i*4-1],每次+i
                for(int j=i;j<=maxx;j=j+i){
                    //y表示区间的个数,如果j+i-1>maxx则取maxx即可,防止数组下标越界
                    long y=num[Math.min(j+i-1,maxx)]-num[j-1];
                    res=(res+(j/i)*y*x)%p;
                }
            }
            return (int)res;
        }
  • 相关阅读:
    【二分图】HEOI2012 朋友圈
    【转载】动态规划—各种 DP 优化
    【默哀】京阿尼纵火案一周年
    【暑假集训】HZOI2019 Luogu P1006 传纸条 二三四维解法
    【暑假集训】HZOI2019 水站 多种解法
    最小二乘法求线性回归方程
    51Nod 最大M子段和系列 V1 V2 V3
    【博弈论】51Nod 1534 棋子游戏
    【最短路】CF 938D Buy a Ticket
    51nod1524 最大子段和V2
  • 原文地址:https://www.cnblogs.com/shoulinniao/p/14775482.html
Copyright © 2011-2022 走看看