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]);
}
}