未见过类型题每周总结(个人向)

1.体操队形

题目

解析

这里我们可以看到测试用例的范围很小,所以我们可以考虑用暴力求解。怎么暴力求解呢?我们可以用决策树的思想,如图:

代码 

import java.util.*;

public class demo3 {//体操队形
    static int[] arr;
    static int ret;
    static boolean[] vis;
    static int n;
    public static void main(String args[]) {
        Scanner in=new Scanner(System.in);
        n=in.nextInt();
        arr=new int[n+1];
        for(int i=1;i<=n;i++) {
            arr[i]=in.nextInt();
        }
        vis=new boolean[n+1];
        search(1);
        System.out.print(ret);
    }
    public static void search(int pos) {
        if(pos==n+1) {//如果枚举到了最后一个也通过了,那么结果就多了一种可能性
            ret++;
            return;
        }
        for(int i=1;i<=n;i++) {
            if(vis[i]) continue;//如果这个数在前面被放过了就换下一个数
            if(vis[arr[i]]) break;//如果这个数的依赖被放过了,那么就没必要看下面了。
            //因为后面总是要放这个数的
            vis[i]=true;//标记为true表示这个数在前面用过了
            search(pos+1);
            vis[i]=false;//标记为false,清理线程,防止影响到下一次循环
        }
    }
}

2.主持人调度(二)

题目

 解析

这里我们可以先按照开始时间排序,这样我们有利于判断活动的开始和结束时间是否重合,建一个小根堆,然后把每一个主持人正在举办的活动的结束时间放进去(至于为什么不考虑前面的活动,是因为都已经举办到当前活动了,就已经没必要考虑之前的活动了),然后让这个结束时间和下一个活动的开始时间比较,如果比堆顶时间还小,那么说明这个堆没有主持人结束活动可以执行当前活动,那么就直接再安排一个主持人,也就是把这个活动的结束时间放入堆中,如果比栈顶时间大,那就说明当前时间至少有一个主持人可以执行当前活动,那么就把堆顶元素去掉,然后加入当前活动的结束时间。这样下来,时间复杂度就是排序O(nlogn)+插入堆O(logn)*元素个数n,也就是2nlogn也就是O(2nlogn),空间复杂度就是堆的大小O(n)。

代码

import java.util.*;
public class demo1 {
    public int minmumNumberOfHost (int n, int[][] startEnd) {
            //排序
        Arrays.sort(startEnd,(a,b)-> {
            //这里很恶心,因为这俩数太大判断大小会出问题,要把它
            //转成long类型
            return (long)a[0]-(long)b[0]>0?1:-1;
        });
        //建一个栈来存放当前所有主持人正在举办的活动的结束时间
        PriorityQueue<Integer> q=new PriorityQueue<>();
        //用ret记录可以让最后不调用size()函数,减少时间
        int ret=1;
        q.offer(startEnd[0][1]);
        for(int i=1;i<n;i++) {
            //如果比最早结束活动的主持人的活动要迟,就把这个
            //活动结束时间更新为新的结束时间
            if(q.peek()<=startEnd[i][0]) {
                q.poll();
                ret--;
            }
            q.offer(startEnd[i][1]);
            ret++;
        }
        return ret;
    }
}

3.二叉树的最大路径和

题目

 
解析

这题的思想是递归,一般二叉树的思想都是递归,或者动态规划,我们可以把一个根节点看作一层,然后求其根节点左串最大的和,再求其又串最大的和,然后把这两个数和0比较,如果大于0就加到根节点上,然后设置一个全局变量为最大的总和,每次都把某个根节点及左右节点的最大和算出和这个变量比较,这个变量取最大值即可。因为我们要的是左右子串中最大的一条直线路径和,所以递归的返回值为两个字节点最大和的最大值。如图:

 代码

import java.util.*;

/*
 * public class TreeNode {
 *   int val = 0;
 *   TreeNode left = null;
 *   TreeNode right = null;
 *   public TreeNode(int val) {
 *     this.val = val;
 *   }
 * }
 */

public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 
     * @param root TreeNode类 
     * @return int整型
     */
     int ret=-0x3f3f3f3f;
    public int maxPathSum (TreeNode root) {
        // write code here
        max(root);
        return ret;
    }
    public int max(TreeNode root) {
        if(root==null) {
            return 0;
        }
        int left=Math.max(0,max(root.left));
        int right=Math.max(0,max(root.right));
        int sum=root.val+left+right;
        if(sum>ret) {
            ret=sum;
        }
        return root.val+Math.max(left,right);
    }
}

4.最长上升子序列(二)


题目

 解析

这题比较复杂,用到了贪心和二叉树,我们直接看图:

 

以上就是解析,这题比较复杂不好表达比较不是视频,如果解析没看懂可以自己按照我说的再模拟一次。

 
代码

import java.util.*;


public class Solution {
    /**
     * 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回方法规定的值即可
     *
     * 该数组最长严格上升子序列的长度
     * @param a int整型一维数组 给定的数组
     * @return int整型
     */
    public int LIS (int[] a) {
        // write code here
        int n = a.length;
        int[] dp = new int[n + 1];
        int pos = 0;
        for (int x : a) {
            if (pos == 0 ||
                    x > dp[pos]) { //当数组中没有数或者x比数组最大的数还大时,直接在下一个位置上填入该数
                dp[++pos] = x;
            }
            //因为我们需要找到离小于x的数相邻的大于等于x的数,所以用二分
            else {
                int l = 1;
                int r = pos;
                while (l < r) {
                    int mid = (l + r) / 2;
                    if (dp[mid] >= x) r = mid;
                    else l = mid + 1;
                }
                dp[l] = x;
            }
        }
        return pos;
    }
}

 5.最长公共子序列(一)

题目

 
解析

一个动态规划问题,一般两个数组的,我们就让i表示前第一个数组的前i项,j表示前第一个数组的前j项,那么dp[i][j]表示当前情况下的最大公共子序列的大小。当s1[i]==s2[j]时dp[i][j]=dp[i-1][j-1]+1否则dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1])为了让初始化方便我们多初始化一组所以s1,s2与dp的对应关系会改变最后输出dp[n][m]即可。

代码

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        char[] s1 = in.next().toCharArray();
        char[] s2 = in.next().toCharArray();
        //dp[i][j]表示s1在前i行s2在第j行前最长的公共子字符串
        //当s1[i]==s2[j]时dp[i][j]=dp[i-1][j-1]+1
        //否则dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1])
        //为了让初始化方便我们多初始化一组
        //所以s1,s2与dp的对应关系会改变
        //最后输出dp[n][m]即可
        int[][] dp = new int[n + 1][m + 1];
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (s1[i - 1] == s2[j - 1]) {
                    dp[i][j] = dp[i - 1][j - 1] + 1;
                } else {
                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
                }
            }
        }
        System.out.print(dp[n][m]);
    }
}

空间优化

因为只需要上一行的信息和当前行的信息,所以我们只需要保存这两行,我们搞两个数组即可

import java.util.*;

// 注意类名必须为 Main, 不要有任何 package xxx 信息
public class Main {
    public static void main(String[] args) {
        Scanner in = new Scanner(System.in);
        int n = in.nextInt();
        int m = in.nextInt();
        char[] s1 = in.next().toCharArray();
        char[] s2 = in.next().toCharArray();
        //dp[i][j]表示s1在前i行s2在第j行前最长的公共子字符串
        //当s1[i]==s2[j]时dp[i][j]=dp[i-1][j-1]+1
        //否则dp[i][j]=Math.max(dp[i-1][j],dp[i][j-1])
        //为了让初始化方便我们多初始化一组
        //所以s1,s2与dp的对应关系会改变
        //最后输出dp[n][m]即可
        int[] dp = new int[m + 1];
        int[] prevdp = new int[m + 1];//表示上一行
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                if (s1[i - 1] == s2[j - 1]) {
                    dp[j] = prevdp[j - 1] + 1;
                } else {
                    dp[j] = Math.max(prevdp[j], dp[j - 1]);
                }
            }
            prevdp = Arrays.copyOf(dp,dp.length);
        }
        System.out.print(dp[m]);
    }
}
优化成果:
优化前:

优化后:

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值