算法刷题记录(一)

目录

算法刷题记录(一)

注:力扣输入所用时间和所占空间默认不算在算法复杂度里;洛谷则默认算.

U265022 006. 和的平方与平方的和之间的差值

题目链接:U265022 006. 和的平方与平方的和之间的差值 - 洛谷

方法一

时间复杂度: O ( n ) O(n) O(n)

空间复杂度: O ( 1 ) O(1) O(1)

思路

1 ∼ n 1\sim n 1n 遍历计算即可.

代码
import java.util.Scanner;

/**
 * @author Re_Gin
 * @link <a href="https://www.luogu.com.cn/problem/U265022">U265022 006. 和的平方与平方的和之间的差值 - 洛谷</a>
 * @date 2023-10-22 19:18:30
 */
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        long ans1 = 1, ans2 = 1;
        for (int i = 2; i <= n; ++i) {
            ans1 += (long) i * i;
            ans2 += i;
        }
    }
}

方法二

时间复杂度: O ( 1 ) O(1) O(1)

空间复杂度: O ( 1 ) O(1) O(1)

思路

用数学公式:

  • 自然数的平方的和 ∑ k = 1 n k 2 = 1 2 + 2 2 + 3 2 + ⋯ + n 2 = n ( n + 1 ) ( 2 n + 1 ) 6 \sum_{k=1}^{n}k^2=1^2+2^2+3^2+\cdots+n^2=\frac{n(n+1)(2n+1)}{6} k=1nk2=12+22+32++n2=6n(n+1)(2n+1)
  • 等差数列前 n n n 项和 1 + 2 + 3 + ⋯ + n = ( 1 + n ) n 2 1+2+3+\cdots+n=\frac{(1+n)n}{2} 1+2+3++n=2(1+n)n
代码
import java.util.Scanner;

/**
 * @author Re_Gin
 * @link <a href="https://www.luogu.com.cn/problem/U265022">U265022 006. 和的平方与平方的和之间的差值 - 洛谷</a>
 * @date 2023-10-22 19:18:30
 */
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        long ans1 = n * (n + 1) * (2L * n + 1) / 6, ans2 = (long) (n + 1) * n / 2;
        System.out.println(ans2 * ans2 - ans1);
    }
}

U265129 008. 序列中的最大乘积

题目链接:U265129 008. 序列中的最大乘积 - 洛谷

分析一下数据范围,判断是否需要用BigInteger.连续 n n n 个数的最大乘积,已知 2 ≤ l e n ≤ 5000 , 2 ≤ n ≤ m i n ( 17 , l e n ) 2\le len\le 5000,2\le n\le min(17,len) 2len5000,2nmin(17,len) ,即 n m a x = 17 n_{max}=17 nmax=17 17 17 17 位的整数最大为 9 17 9^{17} 917,而long的最大值是 2 63 > 9 17 2^{63}>9^{17} 263>917, 所以结果 不会超出long的表示范围.

因此,计算过程选择long类型来存储结果.

方法一

时间复杂度: O ( ( l e n − n ) ∗ n ) O((len-n)*n) O((lenn)n)

空间复杂度: O ( 1 ) O(1) O(1)

思路

遍历第 1 ∼ l e n − n + 1 1\sim len-n+1 1lenn+1 位上的数,每次连续 ∗ 13 *13 13 个数,结果比大小求最大即可.

代码
import java.util.Scanner;

/**
 * @author Re_Gin
 * @link <a href="https://www.luogu.com.cn/problem/U265129">U265129 008. 序列中的最大乘积 - 洛谷</a>
 * @date 2023-10-22 20:15:07
 */
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int len = scan.nextInt();
        scan.nextLine(); // 若不写这一行,s 所读入的长度就会为 0,结果会显示运行错误 RE
        String s = scan.nextLine();
        int n = scan.nextInt();
        long ans = 0;
        for (int i = 0; i <= len - n; ++i) {
            long cur = 1;
            for (int j = 0; j < n; ++j) {
                cur *= s.charAt(i + j) - '0';
            }
            ans = Math.max(ans, cur);
        }
        System.out.println(ans);
    }
}

方法二

时间复杂度: O ( l e n ) O(len) O(len)

空间复杂度: O ( 1 ) O(1) O(1)

思路

双指针优化

  • 在上述计算过程中,我们发现除第一个数外,每个数都计算了多次,那是不是可以选择将 中间数的计算结果 作为一个变量存储下来,每次计算连续的 13 13 13 个数的乘积只需要 除去最前面的一个乘上第 13 13 13 个数 就行了.
  • 那么有什么要注意的点呢?我们需要除去最前面的一个数,那我们怎么知道最前面的那个数是多少呢?所以还需要记录最前面那个数的值;同时,计算过程中用到了除法和乘法,如果除 0 0 0,会发生计算错误,如果乘 0 0 0,再继续运行对当前乘积都不会有影响,所以我们需要 特别判断 0 0 0 的情况,跳过对 0 0 0 的计算.
代码
import java.util.Scanner;

/**
 * @author Re_Gin
 * @link <a href="https://www.luogu.com.cn/problem/U265129">U265129 008. 序列中的最大乘积 - 洛谷</a>
 * @date 2023-10-22 20:15:07
 */
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int len = scan.nextInt();
        scan.nextLine(); // 若不写这一行,s 所读入的长度就会为 0,结果会显示运行错误 RE
        String s = scan.nextLine();
        int n = scan.nextInt();
        long ans = 0;
        long curMul = 1;
        for (int i = 0, j = 0; i < len; ++i) {
            if (s.charAt(i) == '0') {
                while (i < len && s.charAt(i) == '0') ++i;
                if (i == len) break;
                j = i;
                curMul = s.charAt(i) - '0';
            } else {
                curMul *= s.charAt(i) - '0';
                if (i - j + 1 == n) {
                    ans = Math.max(ans, curMul);
                    curMul /= s.charAt(j) - '0';
                    ++j;
                }
            }
        }
        System.out.println(ans);
    }
}

U265594 015. 格子路径

题目链接:U265594 015. 格子路径 - 洛谷

方法一

时间复杂度: O ( ( n + m ) 2 ) O((n+m)^2) O((n+m)2)

空间复杂度: O ( n m ) O(nm) O(nm)

思路

递推公式组合数

总共要走 n + m n+m n+m 步,选出 n n n 步向下走,选出 m m m 步向右走,且组合数 C ( n + m , n ) = C ( n + m , m ) C(n+m,n)=C(n+m,m) C(n+m,n)=C(n+m,m),即:
C a b = C a − 1 b − 1 + C a − 1 b C_a^b=C_{a-1}^{b-1}+C_{a-1}^b Cab=Ca1b1+Ca1b

代码
import java.util.Scanner;

/**
 * @author Re_Gin
 * @link <a href="https://www.luogu.com.cn/problem/U265594">U265594 015. 格子路径 - 洛谷</a>
 * @date 2023-10-22 21:09:13
 */
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt(), m = scan.nextInt(), all = n + m;
        long[][] combination = new long[all + 1][all + 1];
        for (int i = 0; i <= all; ++i) {
            combination[i][0] = combination[i][i] = 1;
            for (int j = 1; j <= i; ++j) {
                combination[i][j] = combination[i - 1][j - 1] + combination[i - 1][j];
            }
        }
        System.out.println(combination[all][n]);
    }
}

方法二

时间复杂度: O ( ( n + m ) ∗ x ) O((n+m)*x) O((n+m)x) x x xBigInteger操作的时间复杂度)

空间复杂度: O ( 1 ) O(1) O(1)

思路

阶乘求组合数

数字太大,应使用BigInteger
C a b = a ! b ! × ( a − b ) ! C_a^b=\frac{a!}{b!×(a-b)!} Cab=b!×(ab)!a!

代码
import java.math.BigInteger;
import java.util.Scanner;

/**
 * @author Re_Gin
 * @link <a href="https://www.luogu.com.cn/problem/U265594">U265594 015. 格子路径 - 洛谷</a>
 * @date 2023-10-22 21:09:13
 */
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt(), m = scan.nextInt(), all = n + m;
        BigInteger num = new BigInteger("1");
        for (int i = 2; i <= all; i++) num = num.multiply(BigInteger.valueOf(i));
        for (int i = 2; i <= n; i++) num = num.divide(BigInteger.valueOf(i));
        for (int i = 2; i <= m; i++) num = num.divide(BigInteger.valueOf(i));
        System.out.println(num);
    }
}

方法三

时间复杂度: O ( n m ) O(nm) O(nm)

空间复杂度: O ( n m ) O(nm) O(nm)

思路

动态规划

因为只能向右或向下移动,所以到达点 ( x , y ) (x,y) (x,y) 的路径数等于到达 ( x − 1 , y ) (x-1,y) (x1,y) 的路径数 + 到达 ( x , y − 1 ) (x,y-1) (x,y1) 的路径数,边界条件是,一旦 x x x 或者 y y y 的坐标为 0 0 0,则到达该点的路径只有 1 1 1 条.

因此,本题动态规划的 状态转移方程
p ( x , y ) = { p ( x − 1 , y ) + p ( x , y − 1 )      x , y > 0 1      x = 0 或 y = 0 p(x,y)=\left\{ \begin{array}{c} p(x-1,y)+p(x,y-1) \ \ \ \ x,y>0 \\ 1 \ \ \ \ x=0或y=0 \\ \end{array} \right. p(x,y)={p(x1,y)+p(x,y1)    x,y>01    x=0y=0

代码
import java.util.Scanner;

/**
 * @author Re_Gin
 * @link <a href="https://www.luogu.com.cn/problem/U265594">U265594 015. 格子路径 - 洛谷</a>
 * @date 2023-10-22 21:09:13
 */
public class Main {
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt(), m = scan.nextInt();
        long[][] dp = new long[n + 1][m + 1];
        for (int i = 0; i <= n; i++) dp[i][0] = 1;
        for (int i = 0; i <= m; i++) dp[0][i] = 1;
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                dp[i][j] = dp[i - 1][j] + dp[i][j - 1];
            }
        }
        System.out.println(dp[n][m]);
    }
}

U273769 020. 阶乘各位数的和

题目链接:U273769 020. 阶乘各位数的和 - 洛谷

方法一

时间复杂度: O ( n + log ⁡ n ! ) O(n+\log_{}{n!}) O(n+logn!)

空间复杂度: O ( log ⁡ n ! ) O(\log_{}{n!}) O(logn!)

思路

先直接求阶乘 n ! n! n!(很大,要使用BigInteger),然后转换成字符串遍历每一位求和即可.

代码
import java.math.BigInteger;
import java.util.Scanner;

/**
 * @author Re_Gin
 * @link <a href="https://www.luogu.com.cn/problem/U273769">U273769 020. 阶乘各位数的和 - 洛谷</a>
 * @date 2023-10-23 17:29:59
 */
public class Main {
    /**
     * 求 n!
     * @param n 正整数
     * @return n!
     */
    public static BigInteger factorial(int n) {
        BigInteger prod = BigInteger.ONE;
        for (int i = 2; i <= n; i++)
            prod = prod.multiply(BigInteger.valueOf(i));
        return prod;
    }

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int n = scan.nextInt();
        String result = factorial(n).toString();
        int sum = 0;
        for (int i = 0; i < result.length(); i++)
            sum += result.charAt(i) - '0';
        System.out.println(sum);
    }
}

U273777 024. 字典排列

题目链接:U273777 024. 字典排列 - 洛谷

方法一

时间复杂度: O ( T ) O(T) O(T)

空间复杂度: O ( T ) O(T) O(T)

思路

深度优先搜索算法

  • (Depth First Search,简称 D F S DFS DFS):一种用于 遍历或搜索树或图 的算法.沿着树的深度遍历树的节点,尽可能深的搜索树的分支。当 节点 v v v 的所在边都己被探寻过或者在搜寻时结点不满足条件,搜索将回溯到发现节点 v v v 的那条边的起始节点.整个进程反复进行直到所有节点都被访问为止.

  • 【参考博客】:DFS入门级(模板)_dfs模型-CSDN博客

代码
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;

/**
 * @author Re_Gin
 * @link <a href="https://www.luogu.com.cn/problem/U273777">U273777 024. 字典排列 - 洛谷</a>
 * @date 2023-10-23 18:37:43
 */
public class Main {
    private static final int nummax = 10; // 0~9 的个数
    private static int count_n = 0; // 第 count_n 个排列数
    private static int[] nums = new int[nummax]; // 一个排列
    private static boolean[] visited = new boolean[nummax];  // 数字是否被选过

    public static void dfs(Map<Integer, String> map, int num) {
        /* 排列数达到10位 */
        if (num == nummax) {
            ++count_n;
            if (map.containsKey(count_n)) {
                StringBuilder str = new StringBuilder();
                for (int i = 0; i < nummax; ++i) {
                    str.append(nums[i]);
                }
                map.put(count_n, String.valueOf(str));
            }
            return;
        }
        for (int i = 0; i < nummax; ++i) {
            if (visited[i]) continue;
            visited[i] = true;
            nums[num] = i;
            dfs(map, num + 1);
            visited[i] = false;
        }
    }

    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        int T = scan.nextInt(), i;
        int[] n = new int[T];
        Map<Integer, String> map = new HashMap<>(); // (n[i],排列)
        for (i = 0; i < T; ++i) {
            n[i] = scan.nextInt();
            map.put(n[i], "WustJavaClub"); // 默认不存在第 n[i] 个数
        }
        dfs(map, 0);
        for (int v : n) {
            System.out.println(map.get(v));
        }
    }
}

注:

  • 简单总结一下 D F S DFS DFS 算法的模板
function dfs(当前状态) {
    if(当前状态 == 目的状态) {
        ···
    }
    for(···寻找新状态) {
        if(状态合法) {
            vis[访问该点]dfs(新状态);
            ?是否需要恢复现场->vis[恢复访问]
        }
    }
    if(找不到新状态) {
        ···
    }
}

452. 用最少数量的箭引爆气球

题目链接:452. 用最少数量的箭引爆气球 - 力扣(LeetCode)

方法一

时间复杂度: O ( n log ⁡ n ) O(n\log_{}{n} ) O(nlogn) n n n 为气球个数)

空间复杂度: O ( log ⁡ n ) O(\log_{}{n} ) O(logn)

思路

排序 + 贪心

代码
class Solution {
    public int findMinArrowShots(int[][] points) {
        if (points.length == 0) return 0;
        Arrays.sort(points, Comparator.comparingInt(a -> a[1]));
        int curPos = points[0][1];
        int ret = 1;
        for (int i = 1; i < points.length; i++) {
            if (points[i][0] <= curPos) {
                continue;
            }
            curPos = points[i][1];
            ++ret;
        }
        return ret;
    }
}

方法二

时间复杂度: O ( n log ⁡ n ) O(n\log_{}{n} ) O(nlogn)

空间复杂度: O ( 1 ) O(1) O(1)

思路

快速排序

代码
class Solution {
    public int findMinArrowShots(int[][] points) {
        quick_sort(points, 0, points.length - 1);
        int ans = 1;
        int right = points[0][1];
        for(int[] point : points){
            if(right < point[0]){
                ++ans;
                right = point[1];
            }
        }
        return ans;
    }

    private void quick_sort(int[][] q, int l, int r){
        if(l >= r) return;
        int x = q[l + r >> 1][1], i = l - 1, j = r + 1;
        while(i < j){
            while(q[++i][1] < x);
            while(q[--j][1] > x);
            if(i < j){
                int[] t = q[i];
                q[i] = q[j];
                q[j] = t;
            }
        }
        quick_sort(q, l, j);
        quick_sort(q, j + 1, r);
    }
}

455. 分发饼干

题目链接:455. 分发饼干 - 力扣(LeetCode)

方法一

时间复杂度: O ( m log ⁡ m + n log ⁡ n ) O(m\log_{}{m}+n\log_{}{n} ) O(mlogm+nlogn)

空间复杂度: O ( log ⁡ m + log ⁡ n ) O(\log_{}{m}+\log_{}{n}) O(logm+logn)

思路

排序 + 双指针 + 贪心

代码
class Solution {
    public int findContentChildren(int[] g, int[] s) {
        Arrays.sort(g);
        Arrays.sort(s);
        int i = 0, j = 0;
        while (i < g.length && j < s.length) {
            if (g[i] <= s[j]) i++;
            j++;
        }
        return i;
    }
}

11. 盛最多水的容器

题目链接:11. 盛最多水的容器 - 力扣(LeetCode)

方法一

时间复杂度: O ( n ) O(n) O(n)

空间复杂度: O ( 1 ) O(1) O(1)

思路
代码
class Solution {
    public int maxArea(int[] height) {
        int l = 0, r = height.length - 1, ans = 0;
        while (l < r) {
            int area = Math.min(height[l], height[r]) * (r - l);
            ans = Math.max(ans, area);
            if (height[l] <= height[r]) ++l;
            else --r;
        }
        return ans;
    }
}

方法二

时间复杂度: O ( n ) O(n) O(n)

空间复杂度: O ( 1 ) O(1) O(1)

思路

剪枝

  • 【剪枝详解】:剪枝 - Cattle_Horse - 博客园 (cnblogs.com)
  • 【说明】:虽然较方法一的时间复杂度没变,但避免了方法一中外循环多次无效重复计算area(严格意义上方法二这不叫剪枝),所以提交时间会快一点.
代码
class Solution {
    public int maxArea(int[] height) {
        int l = 0, r = height.length - 1;
        int maxArea = 0;
        while (l < r) {
            maxArea = Math.max(maxArea, (r - l) * Math.min(height[l], height[r]));
            if (height[l] < height[r]) {
                int curLeftHeight = height[l];
                // 剪枝
                while (height[l] <= curLeftHeight && l < r) {
                    l++;
                }
            } else {
                int curRightHeight = height[r];
                // 剪枝
                while (height[r] <= curRightHeight && l < r) {
                    r--;
                }
            }
        }
        return maxArea;
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值