找规律题动态规划贪心算法,树的题递归。多总结规律题,内核都是规律逻辑。用例子用手推出规律才是重点,实现不重要。
1.arr[i] i j count
2.a.找到第一个大于等于他的数的位置j
b 从i循环到j,count+=min(i,j)-arr[i]
例子:1-2 取最小min(1,2), 2-3取2:a如果中间有值,减去中间的。
c更新i
3 i=len-1
public int trap(int[] height) {
int ans = 0;
int size = height.length;
for (int i = 1; i < size - 1; i++) {
int max_left = 0, max_right = 0;
for (int j = i; j >= 0; j--) { //Search the left part for max bar size
max_left = Math.max(max_left, height[j]);
}
for (int j = i; j < size; j++) { //Search the right part for max bar size
max_right = Math.max(max_right, height[j]);
}
ans += Math.min(max_left, max_right) - height[i];
}
return ans;
}
class Solution {
public int trap(int[] height) {
int res = 0, h1 = 0, h2 = 0;
for (int i = 0, j = height.length - 1; i < height.length && j >= 0; i++, j--) {
h1 = Math.max(h1, height[i]);
h2 = Math.max(h2, height[j]);
res += h1 + h2 - height[i]; // 红色面积 + 蓝色面积 - 黑色面积
}
return res - height.length * h1; // 再减去整个矩形面积
}
}
class Solution {
public int maxCoins(int[] nums) {
int n = nums.length;
int[][] dp = new int[n + 2][n + 2];
int[] val = new int[n + 2];
val[0] = val[n + 1] = 1;
for (int i = 1; i <= n; i++) {
val[i] = nums[i - 1];
}
for (int i = n - 1; i >= 0; i--) {//从后到前,慢慢加多,分解法
for (int j = i + 2; j <= n + 1; j++) {
for (int k = i + 1; k < j; k++) {//k是i、j中间的
int sum = val[i] * val[k] * val[j];
sum += dp[i][k] + dp[k][j];
dp[i][j] = Math.max(dp[i][j], sum);
System.out.println(dp[i][j]);
}
}
}
return dp[0][n + 1];
}
}
package com.sydx.aqsc.ctrl;
public class Main {
public static void main(String[] args) {
isScramble("great", "rgeat");
}
public static boolean isScramble(String s1, String s2) {
char[] chs1 = s1.toCharArray();
char[] chs2 = s2.toCharArray();
int n = s1.length();
int m = s2.length();
if (n != m) {
return false;
}
boolean[][][] dp = new boolean[n][n][n + 1];
// 初始化单个字符的情况
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
dp[i][j][1] = chs1[i] == chs2[j];
}
}
// 枚举区间长度 2~n
for (int len = 2; len <= n; len++) {
// 枚举 S 中的起点位置
for (int i = 0; i <= n - len; i++) {
// 枚举 T 中的起点位置
for (int j = 0; j <= n - len; j++) {
// 枚举划分位置
for (int k = 1; k <= len - 1; k++) {
// 第一种情况:S1 -> T1, S2 -> T2
if (dp[i][j][k] && dp[i + k][j + k][len - k]) {
dp[i][j][len] = true;
break;
}
// 第二种情况:S1 -> T2, S2 -> T1
// S1 起点 i,T2 起点 j + 前面那段长度 len-k ,S2 起点 i + 前面长度k
if (dp[i][j + len - k][k] && dp[i + k][j][len - k]) {
dp[i][j][len] = true;
break;
}
}
}
}
}
return dp[0][0][n];
}
}
package com.sydx.aqsc.ctrl;
import java.util.*;
public class Main {
public static void main(String[] args) {
List<String> r=new ArrayList<>();
r.add("cat");
r.add("cats");
r.add("and");
r.add("sand");
r.add("dog");
wordBreak("catsanddog",r);
}
public static List<String> wordBreak(String s, List<String> wordDict) {
// 为了快速判断一个单词是否在单词集合中,需要将它们加入哈希表
Set<String> wordSet = new HashSet<>(wordDict);
int len = s.length();
// 第 1 步:动态规划计算是否有解
// dp[i] 表示「长度」为 i 的 s 前缀子串可以拆分成 wordDict 中的单词
// 长度包括 0 ,因此状态数组的长度为 len + 1
boolean[] dp = new boolean[len + 1];
// 0 这个值需要被后面的状态值参考,如果一个单词正好在 wordDict 中,dp[0] 设置成 true 是合理的
dp[0] = true;
for (int right = 1; right <= len; right++) {//双指针划分区域
// 如果单词集合中的单词长度都不长,从后向前遍历是更快的
for (int left = right - 1; left >= 0; left--) {
// substring 不截取 s[right],dp[left] 的结果不包含 s[left]
if (wordSet.contains(s.substring(left, right)) && dp[left]) {//核心:区间包含在字典里而且左边也包含的。
dp[right] = true;//给右边坐标设置。dp[i] 表示「长度」为 i 的 s 前缀子串可以拆分成 wordDict 中的单词
// 这个 break 很重要,一旦得到 dp[right] = True ,不必再计算下去
break;
}
}
}
// 第 2 步:回溯算法搜索所有符合条件的解
List<String> res = new ArrayList<>();
if (dp[len]) {
Deque<String> path = new ArrayDeque<>();
dfs(s, len, wordSet, dp, path, res);
return res;
}
return res;
}
/**递归树调用方式
* s[0:len) 如果可以拆分成 wordSet 中的单词,把递归求解的结果加入 res 中
*
* @param s
* @param len 长度为 len 的 s 的前缀子串
* @param wordSet 单词集合,已经加入哈希表
* @param dp 预处理得到的 dp 数组
* @param path 从叶子结点到根结点的路径
* @param res 保存所有结果的变量
*/
private static void dfs(String s, int len, Set<String> wordSet, boolean[] dp, Deque<String> path, List<String> res) {
if (len == 0) {//为零代表就可以分割完毕。
res.add(String.join(" ",path));//路径转成字符串存进结果
return;
}
// 可以拆分的左边界从 len - 1 依次枚举到 0,依次从后向前截取。
for (int i = len - 1; i >= 0; i--) {
String suffix = s.substring(i, len);//截取i到尾处
if (wordSet.contains(suffix) && dp[i]) {//dp用来记忆,减少重复运算,判断该位置可以分割字符串而且包含前缀才进行递归,不符合条件的就进不去。
path.addFirst(suffix);
dfs(s, i, wordSet, dp, path, res);//改变len递归求解
path.removeFirst();//每次结束只需要删掉第一个,进去递归几次就删除几次,最后留下dog,维护
}
}
}
}
1.dp[i][j]
2.
3.i j<len
class Solution {
public int minDistance(String word1, String word2) {
int n = word1.length();
int m = word2.length();
// 有一个字符串为空串
if (n * m == 0) {
return n + m;
}
// DP 数组
int[][] D = new int[n + 1][m + 1];
// 边界状态初始化
for (int i = 0; i < n + 1; i++) {
D[i][0] = i;
}
for (int j = 0; j < m + 1; j++) {
D[0][j] = j;
}
// 计算所有 DP 值
for (int i = 1; i < n + 1; i++) {
for (int j = 1; j < m + 1; j++) {
int left = D[i - 1][j] + 1;
int down = D[i][j - 1] + 1;
int left_down = D[i - 1][j - 1];
if (word1.charAt(i - 1) != word2.charAt(j - 1)) {
left_down += 1;
}
D[i][j] = Math.min(left, Math.min(down, left_down));
}
}
return D[n][m];
}
}
1.dp[i][j](代表出现的不同情况的组合下的解,比如dp[0][1]代表 字符串a 和ca有几个最大公共字符串) str reverse maxEnd maxLen
2.a反转字符串:寻找最长公共子串 b 比较如果相等
b1.dp[i][0]=1, dp[0][j]=1,初始值
b2.dp[i]=dp[i - 1][j - 1] + 1;求最大,递推公式
3.i j
public String longestPalindrome(String s) {
if (s.equals(""))
return "";
String origin = s;
String reverse = new StringBuffer(s).reverse().toString(); //字符串倒置
int length = s.length();
int[][] arr = new int[length][length];
int maxLen = 0;
int maxEnd = 0;
for (int i = 0; i < length; i++)
for (int j = 0; j < length; j++) {
if (origin.charAt(i) == reverse.charAt(j)) {
if (i == 0 || j == 0) {
arr[i][j] = 1;
} else {
arr[i][j] = arr[i - 1][j - 1] + 1;
}
}
if (arr[i][j] > maxLen) {
maxLen = arr[i][j];
maxEnd = i; //以 i 位置结尾的字符
}
}
}
return s.substring(maxEnd - maxLen + 1, maxEnd + 1);
}
暴力求解,列举所有的子串,判断是否为回文串,保存最长的回文串。
public boolean isPalindromic(String s) {
int len = s.length();
for (int i = 0; i < len / 2; i++) {
if (s.charAt(i) != s.charAt(len - i - 1)) {
return false;
}
}
return true;
}
public String longestPalindrome(String s) {
String ans = "";
int max = 0;
int len = s.length();
for (int i = 0; i < len; i++)
for (int j = i + 1; j <= len; j++) {
String test = s.substring(i, j);
if (isPalindromic(test) && test.length() > max) {
ans = s.substring(i, j);
max = Math.max(max, ans.length());
}
}
return ans;
}
1.string i count
2.判断i和i+1是否配对,a是则i+2,继续判断 b不是则i+1,继续判断
DP思路
1.string i count
2. a if判断i是)
b if 例子1(情况1):i-1是(,dp=2;
if 例子2(情况2):i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == ‘(’) 则 dp[i] = dp[i - 1] + 2;
if 例子三:i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == ‘(’)
则 dp[i] = dp[i - 1] + 2;
3.i=len-1
有几个例子就有几个if语句,画出例子实现例子。代码就是代表了逻辑结构,注意嵌套还是并列的。嵌套(分步)是if前面条件一样后面分支,并列(分类)是if前后完全不一样
class Solution {
public int longestValidParentheses(String s) {
int maxans = 0;
int[] dp = new int[s.length()];
for (int i = 1; i < s.length(); i++) {
if (s.charAt(i) == ')') {
if (s.charAt(i - 1) == '(') {
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
} else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
if (i - dp[i - 1]-2 > 0 )
dp[i] = dp[i - 1] + dp[i - dp[i - 1] - 2] + 2;
else dp[i] = dp[i - 1] + 2;
}
maxans = Math.max(maxans, dp[i]);
}
}
return maxans;
}
}
360必须写例子
思路会了就行
1.l1 l2 jinwei_nowjinwei_pre
2.a 反转两个链表:递归方式
b.同时对两个链表更新,并创建结果链表
if加起来 大于10,jinwei_now=1
else 两个节点和+jinwei_pre
c 更新变量jinwei_pre=jinwei_now l1.next l2.next
3. if一个链表为null,另一个全加
错误:没看清题,不需要反转链表
官方思路
1.新建 2.添加队列3出狗,first 4出猫 5.出任意
class AnimalShelf {
LinkedList<int[]> queueCat;
LinkedList<int[]> queueDog;
public AnimalShelf() {
queueCat = new LinkedList<>();
queueDog = new LinkedList<>();
}
public void enqueue(int[] animal) {
// 判断种类后入队
if (animal[1] == 0) {
queueCat.addLast(animal);
} else if (animal[1] == 1) {
queueDog.addLast(animal);
}
}
public int[] dequeueAny() {
// 取出cat的队首,判空则直接返回
int[] headCat;
if (!queueCat.isEmpty()) {
headCat = queueCat.getFirst();
} else if (!queueDog.isEmpty()) {
return queueDog.removeFirst();
} else {
return new int[]{-1,-1};
}
// 取出dog的队首,判空则直接返回
int[] headDog;
if (!queueDog.isEmpty()) {
headDog = queueDog.getFirst();
} else {
return queueCat.removeFirst();
}
// 比较后返回
if (headCat[0]<=headDog[0]) {
return queueCat.removeFirst();
} else {
return queueDog.removeFirst();
}
}
public int[] dequeueDog() {
if (!queueDog.isEmpty()) {
return queueDog.removeFirst();
} else {
return new int[]{-1,-1};
}
}
public int[] dequeueCat() {
if (!queueCat.isEmpty()) {
return queueCat.removeFirst();
} else {
return new int[]{-1,-1};
}
}
}
1.arr[][] i j path
2.a if是1结束,b 是0则添加path,if是最后一个网格, c更新ij
3.len
class Solution {
public List<List<Integer>> pathWithObstacles(int[][] obstacleGrid) {
LinkedList<List<Integer>> list = new LinkedList<>();
dfs(obstacleGrid, list, 0, 0);
return list;
}
public boolean dfs(int[][] obstacleGrid,LinkedList<List<Integer>> list, int x, int y){
if(x < 0 || x >= obstacleGrid.length ||
y < 0 || y >= obstacleGrid[0].length ||
obstacleGrid[x][y] != 0){
return false;
}
obstacleGrid[x][y] = 1; //设置为访问过
list.add(Arrays.asList(x,y)); //添加这个点
if(x == obstacleGrid.length - 1 && y == obstacleGrid[0].length - 1){
return true; //到终点了
}
if(dfs(obstacleGrid, list, x+1, y)){ //是否这条路径可以到终点
return true;
}
if(dfs(obstacleGrid, list, x, y+1)){ //是否这条路径可以到终点
return true;
}
list.removeLast(); //从这个点出发无法到达终点,移除这个点
return false;
}
}
if 结束条件
else 操作 更新
1.定义问题的递归函数,明确函数的功能,我们定义这个函数的功能为:把 A 上面的 n 个圆盘经由 B 移到 C
2.关系公式:move(n from A to C) = move(n-1 from A to B) + move(A to C) + move(n-1 from B to C`)
分析可得,我们分三步走。
(1)先把A上的n-1个圆盘,通过和C的操作移动到B上。
(2)把A剩下的最大的一个盘移动到C。
(3)再把B上的n-1个盘,通过和A的操作移动到C上。
不管怎么移动问题和子问题的关系都可以是这三步,下面就可以开始递归。
class Solution {
public void hanota(List<Integer> A, List<Integer> B, List<Integer> C) {
//得到盘子总数
int n =A.size();
func(n,A,B,C);
}
public void func(int n,List<Integer> A, List<Integer> B, List<Integer> C){
//当盘子都移动空了之后停止递归
if(n<=0){
return;
}
else{
//将A上面的 n-1 个圆盘经由 C 移到 B
func(n-1,A,C,B);
//此时将 A 底下的那块最大的圆盘移到 C
C.add(A.remove(A.size()-1));
// 再将 B 上的 n-1 个圆盘经由A移到 C上
func(n-1,B,A,C);
}
}
}
package 计算机程序算法分类.二叉树问题;
import 牛客网练习题.Solution;
/**
* @Classname 有序数组转变二叉搜索树108
* @Description TODO
* @Date 2021/4/30 14:09
* @Created by xjl
*/
public class 有序数组转变二叉搜索树108 {
public class TreeNode {
int val;
TreeNode left;
TreeNode right;
public TreeNode(int val) {
this.val = val;
}
}
public TreeNode sortedArrayToBST(int[] nums) {
return helper(nums, 0, nums.length - 1);
}
public TreeNode helper(int[] nums, int left, int right) {
if (left > right) {
return null;
}
// 总是选择中间位置左边的数字作为根节点
int mid = (left + right) / 2;
TreeNode root = new TreeNode(nums[mid]);
root.left = helper(nums, left, mid - 1);
root.right = helper(nums, mid + 1, right);
return root;
}
public TreeNode sortedArrayToBSTTest(int[] nums) {
return buidtree(nums, 0, nums.length - 1);
}
private TreeNode buidtree(int[] nums, int left, int right) {
if (left > right) {
return null;
}
int mid = left + (right - left) / 2;
TreeNode node = new TreeNode(nums[mid]);
node.left = buidtree(nums, left, mid - 1);
node.right = buidtree(nums, mid + 1, right);
return node;
}
}
class Solution {
public int pathSum(TreeNode root, int targetSum) {
if (root == null) {
return 0;
}
int ret = rootSum(root, targetSum);
ret += pathSum(root.left, targetSum);
ret += pathSum(root.right, targetSum);
return ret;
}
public int rootSum(TreeNode root, int targetSum) {
int ret = 0;
if (root == null) {
return 0;
}
int val = root.val;
if (val == targetSum) {
ret++;
}
ret += rootSum(root.left, targetSum - val);
ret += rootSum(root.right, targetSum - val);
return ret;
}
}
class Solution {
Map<Integer, Integer> prefixMap;
int target;
public int pathSum(TreeNode root, int sum) {
prefixMap = new HashMap<>();
target = sum;
prefixMap.put(0, 1);
return recur(root, 0);
}
private int recur(TreeNode node, int curSum) {
if(node == null) {
return 0;
}
int res = 0;
curSum += node.val;
res += prefixMap.getOrDefault(curSum - target, 0);
prefixMap.put(curSum, prefixMap.getOrDefault(curSum, 0) + 1);
int left = recur(node.left, curSum);
int right = recur(node.right, curSum);
res = res + left + right;
prefixMap.put(curSum, prefixMap.get(curSum) - 1);
return res;
}
}
操作逻辑: 遍历字符串1.ip2.掩码3.网段。
import java.util.*;
import java.io.*;
public class Main{
public static void main(String[] args) throws IOException{
BufferedReader bf = new BufferedReader(new InputStreamReader(System.in));
int[] count = new int[7];
String in;
while ((in = bf.readLine()) != null) {
//以~分隔
String[] split=in.split("~");
String ip=split[0];//获取IP地址
String mask=split[1];//获取子网掩码
String[] ips = ip.split("\\.");
int num1 = 0;
int num2 = 0;
try{
num1=Integer.parseInt(ips[0]);
num2=Integer.parseInt(ips[1]);
Integer.parseInt(ips[2]);
Integer.parseInt(ips[3]);
}catch(NumberFormatException e){
count[5]++;
continue;
}
//1、先判IP地址是否合法
boolean isIP=isIP(ip);
boolean isMask=isMask(mask);
//如果IP和子网掩码都合法 就判断是哪种类型的IP地址
if(isIP && isMask){
count = countIP(ip,count);
}else if((isIP == false || isMask == false) &&num1 != 127 && num1 != 0) {
count[5]++;
}
}
for(int i=0;i<6;i++)
System.out.print(count[i]+" ");
System.out.print(count[6]);
}
//判断IP是否合法
public static boolean isIP(String IP){
//1. 类似于【0.*.*.*】和【127.*.*.*】的IP地址不属于上述输入的任意一类,也不属于不合法ip地址,计数时可以忽略
String[] IParr = IP.split("\\.");
Boolean isTrue = true;
for (int i = 0; i < IParr.length; i++) {
if (IParr[i].length() <= 0 || IParr[i] == "") {//非法判断
isTrue = false;
}
}
return isTrue;
}
//判断子网掩码是否合法
public static boolean isMask(String mask){
String[] maskarr = mask.split("\\.");
boolean result = false;
//0000 0000~ 1111 1110 八位
//子网掩码为二进制下前面是连续的1,然后全是0。(例如:255.255.255.32就是一个非法的掩码)
// 注意二进制下全是1或者全是0均为非法
int[] maskRange = {254, 252, 248, 240, 224, 192, 128, 0};
List<Integer> list = new ArrayList();
for(int i:maskRange)
list.add(i);
//255.255.255.
if ("255".equals(maskarr[0])&&"255".equals(maskarr[1])&&"255".equals(maskarr[2])) {
if (list.contains(Integer.parseInt(maskarr[3]))) {
result=true;
}
}
//255.255.
else if("255".equals(maskarr[0])&&"255".equals(maskarr[1])){
if(list.contains(Integer.parseInt(maskarr[2]))&&"0".equals(maskarr[3])){
result=true;
}
}
//255.
else if("255".equals(maskarr[0])){
if(list.contains(Integer.parseInt(maskarr[1]))&&"0".equals(maskarr[2])&&"0".equals(maskarr[3])){
result=true;
}
}else if(list.contains(Integer.parseInt(maskarr[0]))){
if("0".equals(maskarr[1])&&"0".equals(maskarr[2])&&"0".equals(maskarr[3])){
result=true;
}
}
return result;
}//method end
//私有IP判断
/*10.0.0.0-10.255.255.255 172.16.0.0-172.31.255.255 192.168.0.0-192.168.255.255*/
public static boolean isPrivateIP(String IP){
String[] split = IP.split("\\.");
//根据范围即可
if(Integer.parseInt(split[0])==10) return true;
else if(Integer.parseInt(split[0])==172&&(Integer.parseInt(split[1])>15&&Integer.parseInt(split[1])<32)){
return true;
}else if(Integer.parseInt(split[0])==192&&Integer.parseInt(split[1])==168){
return true;
}else{
return false;
}
}
//各类ip计数
public static int[] countIP(String IP,int[] count){
String[] split = IP.split("\\.");
int first=Integer.parseInt(split[0]);
//如果是私有ip
if(isPrivateIP(IP)) count[6]++;
//判断其他地址范围
/* A类地址1.0.0.0~126.255.255.255;
B类地址128.0.0.0~191.255.255.255;
C类地址192.0.0.0~223.255.255.255;
D类地址224.0.0.0~239.255.255.255;
E类地址240.0.0.0~255.255.255.255*/
if (first >= 1 && first <= 126)
count[0]++;
else if (first >= 128 && first <= 191)
count[1]++;
else if (first >= 192 && first <= 223)
count[2]++;
else if (first >= 224 && first <= 239)
count[3]++;
else if (first >= 240 && first <= 255)
count[4]++;
return count;
}
}
import java.util.Set;
import java.util.TreeSet;
/*
* 解密9*9数独:
* 格子内填充1-9的数字(空格用0代替),使任意格子的行,列,九宫格都出现1-9的数字
* 例:
* 8 0 0 0 0 0 0 0 0
* 0 0 3 6 0 0 0 0 0
* 0 7 0 0 9 0 2 0 0
* 0 5 0 0 0 7 0 0 0
* 0 0 0 0 4 5 7 0 0
* 0 0 0 1 0 0 0 3 0
* 0 0 1 0 0 0 0 6 8
* 0 0 8 5 0 0 0 1 0
* 0 9 0 0 0 0 4 0 0
* 结果:
* 8 1 2 7 5 3 6 4 9
* 9 4 3 6 8 2 1 7 5
* 6 7 5 4 9 1 2 8 3
* 1 5 4 2 3 7 8 9 6
* 3 6 9 8 4 5 7 2 1
* 2 8 7 1 6 9 5 3 4
* 5 2 1 9 7 4 3 6 8
* 4 3 8 5 2 6 9 1 7
* 7 9 6 3 1 8 4 5 2
*/
public class Main {
static int[][] source = new int[9][9];//原始数组
static int[][] target = new int[9][9];//结果数组
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
/*
* 空格从最上面的行开始,逐行从左向右填充数值
*/
Integer firstX = null;//第一个空格的行号
Integer firstY = null;//第一个空格的列号
/*
* 数组录入输入的数字,并获得第一个空格的行号和列号
*/
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
int c = scanner.nextInt();
source[i][j] = c;
target[i][j] = c;
if(firstX==null && firstY==null && source[i][j]==0){
firstX = i;
firstY = j;
}
}
}
//开始遍历填充
go(firstX,firstY);
//打印结果数组
for(int i=0;i<9;i++){
for(int j=0;j<9;j++){
System.out.print(target[i][j]+" ");
}
System.out.println("");
}
}
public static void go(int x,int y){
boolean flag = build(x,y);//填充
if(flag){//填充成功,获取下一个空格的行号和列号,以源数组为参照
do{
if(x==8&&y==8){//最后的空格填充完毕,结束填充
return;
}
if(y==8){
y=0;
x=x+1;
}else{
y=y+1;
}
}while(source[x][y]!=0);
}else{//填充失败,获取上一个空格的行号和列号,以源数组为参照(因为上一个空格已被填充过,所以需要以源数组作为参照)
do{
if(x==0&&y==0){//退到第一个空格且无可填充的值,证明无解,结束
return;
}
if(y==0){
y=8;
x=x-1;
}else{
y=y-1;
}
}while(source[x][y]!=0);
}
/*
* 1.如果当前空格填充成功,则填充的是下一个空格
* 2.如果当前空格填充失败,则对上一个已填充的空格重新填充
*/
go(x,y);
}
/*
* 1.填充时,优先填充1-9中的较小值
* 2.填充时,会有两种情况:1.新空格填充,空格值等于0 2.因后续空格无法继续填充,老空格
* 尝试填充新的值,再进行后续空格填充
* 3.经过填充后,空格没有获得新值,说明已无合适数字进行填充,需要将空格重置成0,返回f-
* -alse对上一空格进行重新填充
* 4.经过填充后,空格获得新值,返回true根据结果判断对后续空格进行填充
*/
public static boolean build(int x,int y){
int r = target[x][y];
for(int n=r+1;n<10;n++){
target[x][y] = n;
if(isExists(x,y)){
target[x][y] = r;
}else{
break;
}
}
if(target[x][y]==r){
target[x][y]=0;//已无可填充数字,将格子重置成0,并重新填充上一个格子
return false;
}else{
return true;
}
}
//判断当前空格的值是否已存在于同行,同列或同九宫格的其他格子中
public static boolean isExists(int x,int y){
Set<Integer> set = getExists(x,y);
return set.contains(target[x][y]);
}
//获取当前格子的行,列,九宫格中已填充的数字
public static Set getExists(int x, int y){
Set<Integer> set = new TreeSet<Integer>();
for(int i=0;i<9;i++){
if(i==x){
continue;
}
if(target[i][y]==0){
continue;
}
set.add(target[i][y]);
}
for(int i=0;i<9;i++){
if(i==y){
continue;
}
if(target[x][i]==0){
continue;
}
set.add(target[x][i]);
}
int xn = x/3;
int yn = y/3;
for(int i=xn*3;i<xn*3+3;i++){
for(int j=yn*3;j<yn*3+3;j++){
if(i==x&&j==y){
continue;
}
if(target[i][j]==0){
continue;
}
set.add(target[i][j]);
}
}
return set;
}
}
import java.util.*;
import java.lang.*;
public class Main{
private static final String ERROR = "ERROR";
private static final String NONE = "NONE";
private static Map<Integer, String> map2 = new HashMap<Integer, String>() {
{
put(2, "2");
put(3, "3");
put(4, "4");
put(5, "5");
put(6, "6");
put(7, "7");
put(8, "8");
put(9, "9");
put(10, "10");
put(1, "A");
put(11, "J");
put(12, "Q");
put(13, "K");
}
};
public static void main(String[] args) {
Scanner in = new Scanner(System.in);
Map<String, Integer> map = new HashMap<String, Integer>() {
{
put("2", 2);
put("3", 3);
put("4", 4);
put("5", 5);
put("6", 6);
put("7", 7);
put("8", 8);
put("9", 9);
put("10", 10);
put("A", 1);
put("J", 11);
put("Q", 12);
put("K", 13);
put("joker", 0);
put("JOKER", 0);
}
};
while (in.hasNext()) {
List<Integer> list = new ArrayList<>();
for (int i = 0; i < 4; i++) {
String str = in.next();
list.add(map.get(str));
}
if (list.contains(0)) {
System.out.println(ERROR);
} else {
String sb = new String("");
boolean flag = false;
boolean[] used = new boolean[list.size()];
for (int i = 0; i < list.size(); i++) {
sb = String.valueOf(map2.get(list.get(i)));
used[i] = true;
if (helper(list, 1, sb, list.get(i), i, used)) {
flag = true;
break;
}
used[i] = false;
}
if (!flag) {
System.out.println(NONE);
}
}
}
}
private static boolean helper(List<Integer> list, int cnt, String result, int temp, int i, boolean[] used) {
if (cnt == 4) {
if (temp == 24) {
System.out.println(result);
}
return temp == 24;
}
for (int j = 0; j < list.size(); j++) {
if (used[j]) {
continue;
}
used[j] = true;
int y = list.get(j);
if (helper(list, cnt + 1, result + "+" + map2.get(y), temp + y, i, used)) {
return true;
}
if (helper(list, cnt + 1, result + "-" + map2.get(y), temp - y, i, used)) {
return true;
}
if (helper(list, cnt + 1, result + "*" + map2.get(y), temp * y, i, used)) {
return true;
}
if (y != 0 && helper(list, cnt + 1, result + "/" + map2.get(y), temp / y, i, used)) {
return true;
}
used[j] = false;
}
return false;
}
}
class Solution {
public List<String> binaryTreePaths(TreeNode root) {
List<String> paths = new ArrayList<String>();
constructPaths(root, "", paths);
return paths;
}
public void constructPaths(TreeNode root, String path, List<String> paths) {
if (root != null) {
StringBuffer pathSB = new StringBuffer(path);
pathSB.append(Integer.toString(root.val));
if (root.left == null && root.right == null) { // 当前节点是叶子节点
paths.add(pathSB.toString()); // 把路径加入到答案中
System.out.println(pathSB);
} else {
pathSB.append("->"); // 当前节点不是叶子节点,继续递归遍历
System.out.println(pathSB);
constructPaths(root.left, pathSB.toString(), paths);
constructPaths(root.right, pathSB.toString(), paths);
}
}
}
}
## 加入path1.叶子节点2.不是叶子,递归,回溯恢复
class Solution {
String path="";
List<String> paths = new ArrayList<String>();
public List<String> binaryTreePaths(TreeNode root) {
constructPaths(root);
return paths;
}
public void constructPaths(TreeNode root ) {
if (root != null) {
StringBuffer pathSB = new StringBuffer(path);
pathSB.append(Integer.toString(root.val));
if (root.left == null && root.right == null) { // 当前节点是叶子节点
paths.add(pathSB.toString()); // 把路径加入到答案中
System.out.println(pathSB);
} else {
String pat=path;
pathSB.append("->"); // 当前节点不是叶子节点,继续递归遍历
System.out.println(pathSB);
path=pathSB.toString();
constructPaths(root.left );
constructPaths(root.right );
path=pat;
}
}
}
}
class Solution {
static int[][] dirs = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
int[][] grid;
int m, n;
int ans = 0;
public int getMaximumGold(int[][] grid) {
this.grid = grid;
this.m = grid.length;
this.n = grid[0].length;
for (int i = 0; i < m; ++i) {
for (int j = 0; j < n; ++j) {
if (grid[i][j] != 0) {
dfs(i, j, 0);
}
}
}
return ans;
}
public void dfs(int x, int y, int gold) {
gold += grid[x][y];
ans = Math.max(ans, gold);
int rec = grid[x][y];
grid[x][y] = 0;
for (int d = 0; d < 4; ++d) {
int nx = x + dirs[d][0];
int ny = y + dirs[d][1];
if (nx >= 0 && nx < m && ny >= 0 && ny < n && grid[nx][ny] > 0) {
dfs(nx, ny, gold);
}
}
grid[x][y] = rec;
}
}
超时
class Solution {
public int getMaximumGold(int[][] grid) {
m = grid.length;
n = grid[0].length;
this.grid = grid;
//1. 每个格子检查一遍,每个非0格子都可以作为是一条路径的起点
for(int i = 0; i < m; i++){
for(int j = 0; j < n; j++){
//2. 可以作为起点的格子,试试能不能走出更好的路径,更好就替换掉当前的最好路径
if(grid[i][j]!=0)
dfs(i,j,0);
}
}
return ans;
}
int[][] grid;
int m;
int n;
int ans=0;
int[] directions = new int[]{0,1,0,-1,0};
private void dfs(int x, int y, int gold){
if(!(x>=0&&x<m&&y>=0&&y<n&&grid[x][y] > 0)){
return;
}
gold += grid[x][y];
System.out.println(gold);
ans = Math.max(ans, gold);
int rec = grid[x][y];
//3. 走过的格子,先标记为0(代表第二次不可达),尝试完该格子后,再恢复(因为对于前一个格子来说,它是可达,换个方向,进入该格子也是可达,为了换方向再次经过该格子后,能拿到正确的值,第一次查过后,应该恢复原本的值)
grid[x][y] = 0;
dfs(x+1,y,gold);
dfs(x-1,y,gold);
dfs(x,y+1,gold);
dfs(x,y-1,gold);
//3. 走过的格子,先标记为0(代表第二次不可达),尝试完该格子后,再恢复(因为对于前一个格子来说,它是可达,换个方向,进入该格子也是可达,为了换方向再次经过该格子后,能拿到正确的值,第一次查过后,应该恢复原本的值)
grid[x][y] = rec;
}
}
class Solution {
public int calculateMinimumHP(int[][] dungeon) {
return dfs(dungeon, dungeon.length, dungeon[0].length, 0, 0);
}
private int dfs(int[][] dungeon, int m, int n, int i, int j) {
// 到达终点,递归终止。
if (i == m - 1 && j == n - 1) {
return Math.max(1 - dungeon[i][j], 1);
}
// 最后一行,只能向右搜索。
if (i == m - 1) {
return Math.max(dfs(dungeon, m, n, i, j + 1) - dungeon[i][j], 1);
}
// 最后一列,只能向下搜索。
if (j == n - 1) {
return Math.max(dfs(dungeon, m, n, i + 1, j) - dungeon[i][j], 1);
}
// 向下搜索 + 向右搜索,得到(i, j)点的后续路径所要求的最低血量 Math.min(dfs(i + 1, j), dfs(i, j + 1)),
// 又因为(i, j)点本身提供血量dungeon[i][j], 因此从(i, j)开始所需的最低血量为 Math.min(dfs(i + 1, j), dfs(i, j + 1)) - dungeon[i][j]
// 因为骑士的血量不能小于1,因此要和1取个max。
return Math.max(Math.min(dfs(dungeon, m, n, i + 1, j), dfs(dungeon, m, n, i, j + 1)) - dungeon[i][j], 1);
}
}
class Solution {
int[][] memo; // 定义记忆化数组
public int calculateMinimumHP(int[][] dungeon) {
memo = new int[dungeon.length][dungeon[0].length];
return dfs(dungeon, dungeon.length, dungeon[0].length, 0, 0);
}
private int dfs(int[][] dungeon, int m, int n, int i, int j) {
// 到达终点,递归终止。
if (i == m - 1 && j == n - 1) {
return Math.max(1 - dungeon[i][j], 1);
}
// 如果memo数组中有值,直接取出并返回,不进行后续的搜索。
if (memo[i][j] > 0) {
return memo[i][j];
}
// 同解法一,向右搜+向下搜
int minRes = 0;
if (i == m - 1) {
minRes = Math.max(dfs(dungeon, m, n, i, j + 1) - dungeon[i][j], 1);
} else if (j == n - 1) {
minRes = Math.max(dfs(dungeon, m, n, i + 1, j) - dungeon[i][j], 1);
} else {
minRes = Math.max(Math.min(dfs(dungeon, m, n, i + 1, j), dfs(dungeon, m, n, i, j + 1)) - dungeon[i][j], 1);
}
// 将结果存入memo数组
return memo[i][j] = minRes;
}
}