15.神秘字符
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNextLine()) {
int n = Integer.valueOf(sc.nextLine());
for(int i = 0; i < n; i++) {
String a = sc.nextLine();
String b = sc.nextLine();
StringBuilder sb = new StringBuilder();
sb.append(a.substring(0, a.length() / 2));
sb.append(b);
sb.append(a.substring(a.length() / 2, a.length()));
System.out.println(sb);
}
}
}
}
16.位置互换
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int count = sc.nextInt();
sc.nextLine(); //跳到count的下一行
for(int i = 0; i < count; i++) {
String s = sc.nextLine();
StringBuilder sb = new StringBuilder(); //用StringBuilder存储,不用在原先基础上交换
for(int j = 0; j < s.length(); j+=2) {
sb.append(s.charAt(j + 1));
sb.append(s.charAt(j));
}
System.out.println(sb.toString());
}
}
}
17.出栈合法性
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNext()) {
int n = Integer.valueOf(sc.nextLine()); //入栈顺序 1 ~ n
if(n == 0) break;
String[] s = sc.nextLine().split(" ");
int index = 0;
Stack<Integer> stack = new Stack<>();
for(int j = 1; j <= n; j++) { // 1 ~ n入栈
stack.push(j);
// 当前出栈队列的索引位置 等于 栈顶元素时 出栈并使出栈队列索引+1
while(!stack.isEmpty() && stack.peek() == Integer.valueOf(s[index])) {
stack.pop();
index++;
}
}
// 最后看栈是否为空则得知出栈序列是否正确
System.out.println(stack.isEmpty() ? "Yes" : "No");
}
sc.close(); //没有也正确
}
}
18.链表的基本操作
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
LinkedList list = new LinkedList<>();
String[] s = sc.nextLine().split(" "); //notice
list = init(s);
int num = Integer.valueOf(sc.nextLine());
// int initLine = 0;
if(num == 0) return;
while(sc.hasNextLine()) {
String[] ss = sc.nextLine().split(" ");
switch (ss[0]) { //notice
case "get":
System.out.println(list.isEmpty() ? "get fail" : list.get(Integer.valueOf(ss[1]) - 1));
break;
case "delete" :
int del_pos = Integer.valueOf(ss[1]);
if(del_pos > list.size())
System.out.println("delete fail");
else {
for(int i = del_pos; i < list.size(); i++) {
list.set(i - 1, list.get(i)); // notice
}
list.removeLast(); //删除最后一位
System.out.println("delete OK");
}
break;
case "insert" :
int pos = Integer.valueOf(ss[1]);
int data = Integer.valueOf(ss[2]);
if(pos - 1 > list.size())
System.out.println("insert fail");
else if(list.isEmpty()) {
list.add(data);
System.out.println("insert OK");
}else{
list.add(list.getLast());
for(int i = list.size() - 2; i - 1 >= pos - 1; i--) {
list.set(i, list.get(i - 1));
}
list.set(pos - 1, data);
System.out.println("insert OK");
}
break;
case "show" :
if(list.isEmpty())
System.out.println("Link list is empty");
else {
for (int i = 0; i < list.size(); i++) {
System.out.print(list.get(i) + (i == list.size() - 1 ? "" : " ")); //notice 最后一位不用加空格
}
System.out.println();
}
break;
default: break;
}
// if(initLine == num)
// break;
}
sc.close();
}
private static LinkedList init(String[] s) {
LinkedList list = new LinkedList<>();
for(int i = 1; i < s.length; i++) {
list.addFirst(Integer.valueOf(s[i]));
}
return list;
}
}
19.反转链表
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNextLine()) {
String[] str = sc.nextLine().split(" ");
if(Integer.valueOf(str[0]) == 0) {
System.out.println("list is empty");
}
ListNode dummy = new ListNode(-1);
ListNode cur = dummy;
//构造链表
for (int i = 1; i < str.length; i++) {
ListNode temp = new ListNode(Integer.valueOf(str[i]));
cur.next = temp;
cur = cur.next;
if (i == str.length - 1) cur.next = null;
}
//输出原函数
ListNode pointer = dummy.next;
while(pointer != null) {
System.out.print(pointer.val + " ");
pointer = pointer.next;
}
System.out.println();
//反转链表
ListNode prev = null;
ListNode curr = dummy.next; //dummy.next 就是head节点
ListNode temp = null;
while(curr != null) {
temp = curr.next;
curr.next = prev;
prev = curr;
curr = temp;
}
//输出反转链表
ListNode pointer2 = prev; //prev是反转后链表头结点
while(pointer2 != null) {
System.out.print(pointer2.val + " ");
pointer2 = pointer2.next;
}
System.out.println();
}
}
}
class ListNode {
public int val;
public ListNode next;
ListNode(int val) { this.val = val; }
}
20.删除重复元素
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNextInt()) {
int n = sc.nextInt();
if(n != 0) {
int[] arr = new int[n];
for(int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}
print(arr, arr.length);
int t = fun(arr);
print(arr, t);
}else{
System.out.println("list is empty");
break;
}
}
}
private static int fun(int[] arr){
int slow = 1, fast = 1;
while(fast < arr.length) {
if(arr[slow - 1] != arr[fast]) {
arr[slow] = arr[fast];
slow++;
}
fast++;
}
return slow;
}
private static void print(int[] arr, int len) {
for(int i = 0; i < len; i++) {
System.out.print(arr[i]);
if(i != arr.length - 1) {
System.out.print(" ");
}
}
System.out.println();
}
}
21. 构造二叉树(给你中序和前序遍历,写出后序遍历)
import java.util.*;
import java.lang.*;
class TreeNode{
TreeNode left;
TreeNode right;
Character val;
public TreeNode(Character val){
this.val = val;
}
}
public class Main{
static Map<Character, Integer> map;
static StringBuilder sb;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNextLine()) {
String[] input = sc.nextLine().split(" ");
char[] preorder = input[0].toCharArray();
char[] inorder = input[1].toCharArray();
// 构造哈希映射,帮助我们快速定位根节点
map = new HashMap<>();
sb = new StringBuilder();
TreeNode root = buildTree(preorder, inorder);
getPostOrder(root);
System.out.println(sb.toString());
}
}
public static TreeNode buildTree(char[] preorder, char[] inorder) {
for(int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return myBuildTree(preorder, inorder, 0, preorder.length - 1, 0, inorder.length - 1);
}
private static TreeNode myBuildTree(char[] preorder, char[] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return null;
}
// 前序遍历中的第一个节点就是根节点
char preorder_root = preorder[preorder_left];
// 在中序遍历中定位根节点
int inorder_root = map.get(preorder_root);
// 先把根节点建立出来
TreeNode root = new TreeNode(preorder_root);
// 得到左子树中的节点数目
int size_left_subtree = inorder_root - inorder_left;
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root.left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
return root;
}
private static void getPostOrder(TreeNode root) {
if(root == null) return;
getPostOrder(root.left);
getPostOrder(root.right);
sb.append(root.val);
}
}
22.二叉树的遍历
// 方法二:使用索引,简化构建树的过程
import java.util.Scanner;
public class Main {
static class TreeNode {
char val;
int left;
int right;
public TreeNode(char val, int left, int right) {
this.val = val;
this.left = left;
this.right = right;
}
}
static TreeNode[] nodes;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
nodes = new TreeNode[n + 1];
for (int i = 0; i < n; i++) {
char val = sc.next().charAt(0);
int left = sc.nextInt();
int right = sc.nextInt();
nodes[i + 1] = new TreeNode(val, left, right);
}
preOrderTraversal(1);
System.out.println();
inOrderTraversal(1);
System.out.println();
postOrderTraversal(1);
System.out.println();
sc.close();
}
private static void postOrderTraversal(int root) {
if (root == 0)
return;
postOrderTraversal(nodes[root].left);
postOrderTraversal(nodes[root].right);
System.out.print(nodes[root].val);
}
private static void inOrderTraversal(int root) {
if (root == 0)
return;
inOrderTraversal(nodes[root].left);
System.out.print(nodes[root].val);
inOrderTraversal(nodes[root].right);
}
private static void preOrderTraversal(int root) {
if (root == 0)
return;
System.out.print(nodes[root].val);
preOrderTraversal(nodes[root].left);
preOrderTraversal(nodes[root].right);
}
}
23.二叉树的高度
import java.util.*;
import java.lang.*;
class TreeNode{
TreeNode left;
TreeNode right;
Character val;
public TreeNode(Character val) {
this.val = val;
}
}
public class Main{
static Map<Character, Integer> map;
static StringBuilder sb;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNextLine()) {
sc.nextLine();
char[] preorder = sc.nextLine().toCharArray();
char[] inorder = sc.nextLine().toCharArray();
// 构造哈希映射,帮助我们快速定位根节点
map = new HashMap<>();
sb = new StringBuilder();
TreeNode root = buildTree(preorder, inorder);
System.out.println(getHeight(root));
}
}
public static TreeNode buildTree(char[] preorder, char[] inorder) {
for(int i = 0; i < inorder.length; i++) {
map.put(inorder[i], i);
}
return myBuildTree(preorder, inorder, 0, preorder.length - 1, 0, inorder.length - 1);
}
private static TreeNode myBuildTree(char[] preorder, char[] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return null;
}
// 前序遍历中的第一个节点就是根节点
char preorder_root = preorder[preorder_left];
// 在中序遍历中定位根节点
int inorder_root = map.get(preorder_root);
// 先把根节点建立出来
TreeNode root = new TreeNode(preorder_root);
// 得到左子树中的节点数目
int size_left_subtree = inorder_root - inorder_left;
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root.left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
return root;
}
private static int getHeight(TreeNode root){
if(root == null) {
return 0;
}else{
return Math.max(getHeight(root.left), getHeight(root.right)) + 1;
}
}
}
24.最长公共子序列
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
while(sc.hasNextLine()) {
String[] str = sc.nextLine().split(" ");
String str1 = str[0];
String str2 = str[1];
char[] s = str1.toCharArray();
char[] t = str2.toCharArray();
int n = s.length;
int m = t.length;
int[][] f = new int[n + 1][m + 1];
for(int i = 0; i < n; ++i) {
for(int j = 0; j < m; ++j) {
f[i + 1][j + 1] = s[i] == t[j] ? f[i][j] + 1 : Math.max(f[i][j + 1], f[i + 1][j]);
}
}
System.out.println(f[n][m]);
}
}
}
26.不相同的字符串
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
sc.nextLine();
for(int i = 0; i < n; i++) {
String str = sc.nextLine();
System.out.println(minOperations(str));
}
}
public static int minOperations(String str) {
int[] hashMap = new int[26];
//初始化数组
for(int i = 0; i < str.length(); i++) { //遍历字符串
int position = str.charAt(i) - 'a'; //寻找字符对应数组中的位置
hashMap[position]++;
}
int charUserCount = 0;// 记录 26 个位置被使用的次数
int result = 0;// 需要操作的次数
// 遍历数组
for(int i = 0; i < hashMap.length; i++) {
if(hashMap[i] == 1) { // 字符只出现一次,不需要操作,占用掉一个位置
charUserCount++;
}
if(hashMap[i] % 2 == 0) { // 字符出现偶数次,需要操作 n/2 次,并且占用 n / 2 个位置
charUserCount += hashMap[i] / 2;
result += hashMap[i] / 2;
}
if(hashMap[i] != 1 && hashMap[i] % 2 == 1) { // 字符出现奇数次,需要操作 n/2 次,并且占用 n / 2 + 1 个位置
charUserCount += hashMap[i] / 2 + 1;
result += hashMap[i] / 2;
}
}
// 当所有字母都被占用的时候,那么就进入到第二种情况
// 把所有多出来的字符全都转化成某一个字母,比如a
// 此时的情况一定是a有n个,其他字母全是1个,我们只需要消除多余的a即可
// 每次删掉两个a,再转化成一个a,这样操作一次就少一个a
// 总会变成所有字母都只剩下一个的情况,即达成题意不重复
if(charUserCount > 26) {
result += charUserCount - 26;
}
return result;
}
}
27.最长增长子序列
import java.util.*;
public class Main{
public static void main (String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
sc.nextLine();
for(int i = 0;i< n;i++){
String s = sc.nextLine();
s = s.replaceAll("[\\[\\]]","");
String[] sArray = s.split(",");
int[] nums = new int[sArray.length];
for(int j = 0;j < sArray.length;j++){
nums[j] = Integer.parseInt(sArray[j]);
}
printMaxLen(nums);
}
}
public static void printMaxLen(int[] nums){
int n = nums.length, ans = 0;
int[] f = new int[n];
for(int i = 0; i < n; i++) {
for(int j = 0; j < i; j++) {
if(nums[j] < nums[i]) {
f[i] = Math.max(f[i], f[j]);
}
}
ans = Math.max(ans, ++f[i]);
}
System.out.println(ans);
// int n = nums.length;
// if(n == 0) return;
// int[] d = new int[n];
// Arrays.fill(d,1);
// int res = 1;
// for(int i = 1;i < n;i++){
// //必须和前面所有进行比较,注意初始值
// for(int j = 0;j < i;j++){
// if(nums[i] > nums[j]){
// d[i] = Math.max(d[i],d[j] + 1);
// }
// }
// res = Math.max(res,d[i]);
// }
// System.out.println(res);
}
}
31.字符串的最大价值
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
int[] count = new int[2]; //记录字符 0 ,1 的个数
for(char c : s.toCharArray()) {
count[c - '0']++;
}
System.out.println(Math.max(getValue('0', s, count), getValue('1', s, count)));
}
public static int getValue(char c, String s, int[] count) {
int start = s.indexOf(c);
int end = s.lastIndexOf(c);
int num = (1 + count[c - '0']) * count[c - '0'] / 2;
num += (1 + start) * start / 2;
num += (s.length() - end) * (s.length() - end - 1) / 2;
return num;
}
}
/*
首先,使用 count 数组来记录字符 '0' 和 '1' 的个数。
对于每个字符 '0' 和 '1',都分别计算以其为分界的情况下的价值。
对于以字符 '0' 为分界的情况:
1. 找到第一个字符 '0' 出现的位置 start 和最后一个字符 '0' 出现的位置 end。
2. 计算连续字符 '0' 所对应的价值:(1 + count[c - '0']) * count[c - '0'] / 2,其中 count[c - '0'] 是字符 '0' 的个数。也就是将 '0' 之间的所有 ‘1’ 逻辑上删除。
3. 计算字符 '0' 之前连续字符 '1' 所对应的价值:(1 + start) * start / 2。
4. 计算字符 '0' 之后连续字符 '1' 所对应的价值:(s.length() - end) * (s.length() - end - 1L) / 2,这里要注意长度减1。
同样的,对于以字符 '1' 为分界的情况,重复上述步骤。
最后,从这两种情况中选择较大的一个作为最终的最大价值。
*/
33.逛街(单调栈问题)
import java.util.*;
/*
题意为计算在每栋楼的位置处可以看到多少栋楼,考虑了从左向右和从右向左两个方向的可见性。可以使用单调栈的特性进行解决。
这里是代码的主要思路:
首先将 ans 每个位置上的值加 1(因为包括自己本身的楼)。
从左向右遍历楼的高度数组 buildings,使用栈 stack1 来保存之前遇到的楼的高度,
保证栈顶元素总是比后面的元素要高。在遍历过程中,对于每一栋楼,
将栈的大小(即之前楼的数量)存储在 ans 数组中的对应位置,
表示往左看能看到的楼的数量。如果当前楼高度大于等于栈顶元素,则不断将栈顶元素弹出
,直到满足条件。
从右向左遍历楼的高度数组 buildings,使用栈 stack2 来保存之前遇到的楼的高度,
保证栈顶元素总是比后面的元素要高。在遍历过程中,对于每一栋楼,将栈的大小
(即之前楼的数量)加到 ans 数组中的对应位置,表示往右看能看到的楼的数量
。如果当前楼高度大于等于栈顶元素,则不断将栈顶元素弹出,直到满足条件。
*/
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String str = scanner.nextLine();
int[] buildings = parseIntArray(str);
int n = buildings.length;
int[] ans = new int[n];
Arrays.fill(ans, 1);
calculateVisibleCounts(buildings, ans);
System.out.println(Arrays.toString(ans).replaceAll(" ", ""));
}
public static void calculateVisibleCounts(int[] buildings, int[] ans) {
int n = buildings.length;
Deque<Integer> stack1 = new ArrayDeque<>();
Deque<Integer> stack2 = new ArrayDeque<>();
for (int i = 0; i < n; i++) { //从左到右
ans[i] += stack1.size();
while (!stack1.isEmpty() && stack1.peek() <= buildings[i]) {
stack1.pop();
}
stack1.push(buildings[i]);
}
for (int i = n - 1; i >= 0; i--) { //从右到左
ans[i] += stack2.size();
while (!stack2.isEmpty() && stack2.peek() <= buildings[i]) {
stack2.pop();
}
stack2.push(buildings[i]);
}
}
public static int[] parseIntArray(String input) {
String[] str = input.substring(1, input.length() - 1).split(",");
int[] arr = new int[str.length];
for (int i = 0; i < str.length; i++) {
arr[i] = Integer.parseInt(str[i]);
}
return arr;
}
}
34.大鱼吃小鱼
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
sc.nextLine();
List<Integer> list = new LinkedList<>();
for(int i = 0; i < n; i++) {
list.add(sc.nextInt());
}
int count = 0;
while(!isIncreasing(list)) { //递增就是终止条件
for(int i = list.size() - 1; i >= 1; i--) {
if(list.get(i) < list.get(i - 1)) { // 如果右边的小于左边的,会被吃掉
list.remove(i);
}
}
count++;
}
System.out.println(count);
}
public static boolean isIncreasing(List<Integer> list) {
for(int i = 1; i < list.size(); i++) {
if(list.get(i - 1) > list.get(i)) {
return false;
}
}
return true;
}
}
35.打印二维数组
/**
* 思路:
* 第一步:
* 1. 沿着第一行从左到右进行循环
* 2. 然后沿着从右上到左下的斜线进行填充
* ->
* [1 ][2 ][4 ][0 ]
* [3 ][5 ][0 ][0 ] ↙
* [6 ][0 ][0 ][0 ]
* [0 ][0 ][0 ][0 ]
*
* 第二步:
* 1. 沿着最后一列从上到下进行循环
* 2. 仍然沿着右上到左下的斜线进行填充
* |
* v
* [1 ][2 ][4 ][7 ]
* [3 ][5 ][8 ][11] ↙
* [6 ][9 ][12][14]
* [10][13][15][16]
*
* 思路有了,接下来需要解决的问题:
* 1. 填充的数字是多少
* 从规律上看数字是递增的,可以使用一个 count 变量
* 进行标记,填充一次 +1
* 2. 外层循环和内层循环的条件
* 第一步:
* 外层循环:第一行从左到右循环到最后一列
* 内层循环:填充的元素位置不越界就可以一直沿着
* 斜线进行填充
* 第二步:
* 外层循环:最后一列从上到下循环到最后一行
* 内层循环:同第一步
*/
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[][] array = new int[n][m];
int count = 1;
//第一步
//第一行从左到右进行遍历(不包括对角线)
for(int i = 0; i < m - 1; i++) {
int currentY = i;
int currentX = 0;
//保证填充的元素位置不越界
while(currentX != n && currentY != - 1) {
array[currentX][currentY] = count;
count++;
currentX++;
currentY--;
}
}
//第二步
//最后一列从上到下进行遍历(包括对角线)
for(int i = 0; i < n; i++) {
int currentX = i;
int currentY = m - 1;
while(currentY != -1 && currentX != n) {
array[currentX][currentY] = count;
count++;
currentX++;
currentY--;
}
}
//打印数组
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
System.out.printf("%d ", array[i][j]); //%d : 输出十进制整数
}
System.out.println();
}
}
}
36.网格路径和
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String arrayString = sc.next();
int[][] grid = parse2dArray(arrayString);
System.out.println(maxPathSum(grid));
}
public static int maxPathSum(int[][] grid) {
int n = grid.length;
int m = grid[0].length;
int[][] dp = new int[n][m];
dp[0][0] = grid[0][0];
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
if(i == 0 && j == 0) continue; //起点
else if(i == 0) {
dp[i][j] = dp[i][j - 1] + grid[i][j];
}else if(j == 0){
dp[i][j] = dp[i - 1][j] + grid[i][j];
}else
dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]) + grid[i][j];
}
}
return dp[n - 1][m - 1];
}
// 将字符串解析为二维数组
public static int[][] parse2dArray(String arrayString) {
String[] strs = arrayString.substring(2, arrayString.length() - 2).split("],");
// "[[1,2,3],[2,3,4],[3,4,5]]" -> "1,2,3", "[2,3,4", "[3,4,5"
int rows = strs.length;
int cols = strs[0].split(",").length;
int[][] grid = new int[rows][cols];
for(int i = 0; i < rows; i++) {
String str = strs[i].replaceAll("\\[", ""); //去除所有左中括号
// "[3,4,5" - > "3,4,5"
String[] elements = str.split(",");
// "3,4,5" - > 3 4 5
for(int j = 0; j < cols; j++) {
grid[i][j] = Integer.parseInt(elements[j]);
}
}
return grid;
}
}
37.交换字符
import java.util.*;
/**
* 思路:贪心算法
*
* 首先对 01 串进行分析,当字符串长度为奇数时,最终的字符串
* 一定以比较多的那个字符开头结尾。当字符串长度为偶数时,最终的
* 字符串开头可能是'1' 也有可能是 '0'。此时,我们已经知道了
* 目标字符串的样子。
*
* 下一步的问题是如何以最小的次数将当前的字符串通过题目中的
* 相邻字符相互交换的操作变为目标字符串。
*
* 将一个字符从 i 位置移动到 j 位置,所需最低的次数是abs(i - j)
* i 是当前位置,j 是最终位置。
*
* 明确了最终的目标字符串,以及移动的次数了以后,选择的贪心策略是
* 从头开始遍历字符串,选择目标字符,将其移动到最近的目标位置,
* 记录其花费的次数。
*
* 这个贪心策略的合理性在于,让每个字符都移动到距离它最近的位置,
* 通过局部最优的移动策略得到总体最优的移动策略。
*
* 以'1'和'0'分别为目标字符遍历一次后,
* 根据二者的数量关系,确定最终的答案。
*/
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.next();
// 统计字符串中 '0' 和 '1' 的出现次数
int count0 = 0;
int count1 = 0;
for(int i = 0; i < s.length(); i++) {
if(s.charAt(i) == 0)
count0++;
else
count1++;
}
long swap0 = countSwap(s, '0');
long swap1 = countSwap(s, '1');
if(count0 > count1){
System.out.println(swap0);
}else if(count0 < count1) {
System.out.println(swap1);
}else{
System.out.println(Math.min(swap1,swap0));
}
}
public static long countSwap(String s, char c) {
int currentSwapIndex = 0; //需要交换到的索引位置
long swaps = 0; //交换次数统计
for(int i = 0; i < s.length(); i++) {
if(s.charAt(i) == c) {
swaps += Math.abs(i - currentSwapIndex);
currentSwapIndex += 2;
}
}
return swaps;
}
}
38.填充矩阵(螺旋矩阵)
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[][] matrix = generateMatrix(n, m);
printMatrix(matrix, n, m);
}
public static int[][] generateMatrix(int n, int m) {
int[][] res = new int[n][m];
int u = 0;
int d = n - 1;
int l = 0;
int r = m - 1;
int num = n * m + 1;
while(true) {
for(int i = l; i <= r; i++) res[u][i] = --num;
if(++u > d) break;
for(int i = u; i <= d; i++) res[i][r] = --num;
if(--r < l) break;
for(int i = r; i >= l; i--) res[d][i] = --num;
if(--d < u) break;
for(int i = d; i >= u; i--) res[i][l] = --num;
if(++l > r) break;
}
return res;
}
public static void printMatrix(int[][] matrix, int n, int m) {
for(int i = 0; i < n; i++) {
for(int j = 0; j < m; j++) {
System.out.printf("%d ", matrix[i][j]);
}
System.out.println();
}
}
}
39.求和(回溯)
import java.util.*;
/**
* 思路:回溯算法
*
* 确定递归函数的参数列表
*
* backtracking(int index, int sum)
* index: 用于标记当前编辑到数的位置
* sum: 当前累积的和
*
* 终止条件
*
* 当 sum 大于所需要的值时就终止
*
* 遍历方式:
*
* 按照树形结构的遍历方式进行遍历
*/
public class Main{
static ArrayList<Integer> list;
static int m, n;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
n = sc.nextInt();
m = sc.nextInt();
list = new ArrayList<>();
backtracking(1, 0);
list.clear();
}
// 回溯函数,index 是当前要考虑的数字,sum 是当前组合的和
public static void backtracking(int index, int sum){
if(sum > m) {
return; // // 如果当前和已经大于目标和,返回,不再继续搜索
}
if(sum == m) {
// 如果当前和等于目标和,打印当前组合
for(int i = 0; i < list.size() - 1; i++) {
System.out.print(list.get(i) + " ");
}
System.out.println(list.get(list.size() - 1));
}
for(int i = index; i <= n && sum + i <= m; i++) {
// 从当前数字开始尝试添加到组合中
list.add(i); // 添加当前数字到组合中
backtracking(i + 1, sum + i); // 递归搜索下一个数字
list.remove(list.size() - 1); // 回溯,将当前数字移出组合
}
}
}
40.到达目的地的最短距离(动态规划)
/**
* 思路:使用动态规划解决
*
* 确定 dp 数组以及下标的含义
*
* 设 dp[i] 表达到 i 点的最少步数
*
* 考虑状态转移方程
*
* 一、如果当前的位置能够被 2 整除,只有一种走法
* 从别的位置,通过在数轴上移动到当前位置数值的两倍方式移动过来的。
* dp[i] = dp[i / 2] + 1;
*
* 二、如果当前的位置不能被 2 整数,有两种走法
* 1. 通过在数轴上向前移动一格的方式移动过来的
* 2. 通过在数轴上向后移动一格的方式移动过来的
*
* dp[i] = Math.min(dp[i - 1], dp[(i + 1) / 2] + 1) + 1
*
* 第 1 种走法为 dp[i - 1] 不难理解
* 为什么第 2 种走法会是 dp[(i + 1) / 2] + 1 呢?
* 因为如果当前位置不能被整除,那么这个位置的后一个位置必定能被整除,能被整除的坐标参考能被整除的走法。
*
* 数组初始化
*
* 1. 不要移动的情况 dp[0] = 0
* 2. 只移动一步的情况 dp[1] = 1
*
* 遍历顺序
*
* 遍历方式比较简单,从前向后遍历就可以
*/
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int x = sc.nextInt();
System.out.println(solve(x));
}
public static int solve(int x) {
if(x < 2 && x >= 0) {
return x;
}
if(x < 0) {
x = -x; //负整数结果与正整数结果一样
}
int[] dp = new int[x + 1];
dp[0] = 0;
dp[1] = 1;
for(int i = 2; i <= x; i++) {
if(i % 2 == 0) {
dp[i] = dp[i / 2] + 1;
}else{
dp[i] = Math.min(dp[i - 1], dp[(i + 1) / 2] + 1) + 1;
}
}
return dp[x];
}
}
41.岛屿数量
import java.util.*;
public class Main{
static int[][] grid;
static int res;
public static void main(String[] args) {
res = 0;
Scanner sc = new Scanner(System.in);
int m = sc.nextInt();
int n = sc.nextInt();
int count = sc.nextInt();
grid = new int[m][n];
for(int i = 0; i < count; i++) { //每次输入一个坐标,输入count次
grid[sc.nextInt()][sc.nextInt()] = 1;
for(int j = 0; j < m; j++) {
for(int k = 0; k < n; k++) {
if(grid[j][k] == 1) {
dfs(grid, j, k);
res++;
}
}
}
System.out.printf("%d ", res);
for(int j = 0; j < m; j++) { //将每一次的坐标标记为2的进行还原
for(int k = 0; k < n; k++) {
if(grid[j][k] == 2) {
grid[j][k] = 1;
}
}
}
res = 0; //重置每一次的res
}
}
public static void dfs(int[][] grid, int r, int c) {
if(!inArea(grid, r, c)) {
return;
}
if(grid[r][c] != 1) { //1表示陆地(未遍历)
return ;
}
grid[r][c] = 2; //2表示陆地(已遍历)
dfs(grid, r - 1, c);
dfs(grid, r + 1, c);
dfs(grid, r, c - 1);
dfs(grid, r, c + 1);
}
public static boolean inArea(int[][] grid, int r, int c) {
return r >= 0 && r < grid.length && c >= 0 && c < grid[0].length;
}
}
42.汽水瓶换饮料
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {
int emptyBottles = scanner.nextInt();
if (emptyBottles == 0) {
break;
}
System.out.println(maxSodaBottles(emptyBottles));
}
}
public static int maxSodaBottles (int n) {
int totalSodas = 0;
while (n >= 3) {
int newSodas = n / 3; // 手里的空瓶能够换取多少瓶饮料
totalSodas += newSodas; // 喝掉用兑换的所有饮料
n = n % 3 + newSodas; // 新的空瓶总数 = 未能兑换的空瓶 + 喝完饮料后剩下的空瓶
}
if (n == 2) { // 还剩两个空瓶的时候找老板借一个空瓶
totalSodas += 1;
}
return totalSodas;
}
}
44.开发商购买土地
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
int[][] arr = new int[n][m];
int count = 0; //所有的元素和
for(int i = 0; i < n; i++) { //转换为二维数组求解
for(int j = 0; j < m; j++) {
arr[i][j] = sc.nextInt();
count += arr[i][j];
}
}
int a = 0;
int b = 0;
int res = Integer.MAX_VALUE;
int res1 = Integer.MAX_VALUE;
int res2 = Integer.MAX_VALUE;
int remain = 0; //另一半的值, a为一边的值
for(int j = 0; j < n; j++) { //按照 列 分
for(int k = 0; k < m; k++) {
a += arr[j][k];
}
remain = count - a;
res1 = Math.min(res1, Math.abs(remain - a));
}
for(int j = 0; j < m; j++) { //按照 列 分
for(int k = 0; k < n; k++) {
b += arr[k][j];
}
remain = count - b;
res2 = Math.min(res2, Math.abs(remain - b));
}
res = Math.min(res1, res2);
System.out.println(res);
}
}
45.虚拟棋盘对战(动态规划)
/**
* 思路:动态规划
* 用F[l][r]表示先选的人能拿到的最高分
* 用S[l][r]来表示后选的人能拿到的最高分
* 对于先选者,有两种选法
* 若先选者选A[0],则对于后面的1, ... ,n-1 数组,他就变成了后选者,此时能拿到的分为A[0]+S[1][n-1]
* 若先选者选A[n-1],则对于前面的数组0,...,n-2,同样变为后选者,此时能拿到得分为A[n-1]+S[0][n-2];
* 所以 F[0][n-1] = max(A[0]+S[1][n - 1],A[n - 1]+S[0][n - 2])
* 对于后选者,他能能到的最高分是受先选者控制的,即他只能选到先选者留给他的最小值,将其转化为数学形式就是
* S[l][r] = min(F[l + 1][r], F[l][r - 1]),因为先选者很聪明,肯定会把最低的分数留给他
*/
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] array = new int[n];
for(int i = 0; i < n; i++) {
array[i] =sc.nextInt();
}
System.out.println(findMaxScore(array, n));
}
public static int findMaxScore(int[] A, int n) {
int[][] F = new int[n][n]; //用F[l][r]表示先选的人能拿到的最高分
int[][] S = new int[n][n]; //用S[l][r]表示后选的人能拿到的最高分
for(int r = 0; r < n; r++) {
F[r][r] = A[r]; // [a][b]表示范围 a - b
S[r][r] = 0;
for(int l = r - 1; l >= 0; l--) { //从右往左遍历 l--;
F[l][r] = Math.max(A[l] + S[l+ 1][r], A[r] + S[l][r - 1]);
S[l][r] = Math.min(F[l + 1][r], F[l][r - 1]);
}
}
return Math.max(F[0][n - 1], S[0][n - 1]);
}
}
46.携带研究材料(01背包问题)
一维DP
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
// 读取 N
Scanner scanner = new Scanner(System.in);
int M = scanner.nextInt();
int V = scanner.nextInt();
int[] weights = new int[M];
int[] values = new int[M];
for (int i = 0; i < M; i++) {
weights[i] = scanner.nextInt();
}
for (int j = 0; j < M; j++) {
values[j] = scanner.nextInt();
}
// 创建一个动态规划数组dp,初始值为0
int[] dp = new int[V + 1];
// 外层循环遍历每个类型的研究材料
for (int i = 0; i < M; ++i) {
// 内层循环从 N 空间逐渐减少到当前研究材料所占空间
for (int j = V; j >= weights[i]; --j) {
// 考虑当前研究材料选择和不选择的情况,选择最大值
dp[j] = Math.max(dp[j], dp[j - weights[i]] + values[i]);
}
}
// 输出dp[N],即在给定 N 行李空间可以携带的研究材料最大价值
System.out.println(dp[V]);
}
}
二维DP
import java.util.*;
public class Main {
public static void main(String[] args) {
// 背包容量 N
// 物品种类 M
Scanner sc = new Scanner(System.in);
int M = sc.nextInt();
int N = sc.nextInt();
int[] values = new int[M];
int[] weights = new int[M];
for(int i = 0; i < M;i++) {
weights[i] = sc.nextInt();
}
for(int i = 0; i < M;i++) {
values[i] = sc.nextInt();
}
int[][] dp = new int[M][N+1];
// 初始化
for(int i = weights[0]; i <= N; i++) { //重点中的重点
dp[0][i] = values[0]; //初始化第一行(不同容量只选第一个物品)
} //其余第一列及其容量小于第一个物品容量的都为0
// 先物品
for(int i = 1; i < M; i++) { // 遍历科研物品
// 后背包
for(int j = 0; j <= N; j++) { // 遍历行李箱容量
// 如果装不下这个物品,那么就继承dp[i - 1][j]的值
if(weights[i] > j) {
dp[i][j] = dp[i-1][j];
} else {
dp[i][j] = Math.max(dp[i-1][j],dp[i-1][j-weights[i]] + values[i]);
}
}
}
System.out.println(dp[M-1][N]);
}
}
48.安排讲座
/**
* 思路:贪心算法
*
* 贪心的思路是每次选择局部最优解,最终达到全局最优解。
* 在这个问题中,我们希望尽可能多的安排讲座,因此我们需要选择班级的课余时间段来最大化讲座的数量。
*
* 首先,对班级课余时间段进行排序,按结束时间从小到大排序。
* 这是希望选择结束时间早的班级,以便留更多的时间给其他班级。
*
* 其次,初始化一个计数器 count 为 0,表示当前安排的讲座为 0,
* 以及一个变量 end,表示当前已经安排的讲座的结束时间。
*
* 遍历排序后的课余时间段信息:
* if (当前班级的开始时间 >= 已经安排的讲座结束时间) {
* 安排的讲座数量++;
* 将已经安排的讲座结束时间设置为当前班级的结束时间;
* }
*/
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[][] schedules = new int[n][2];
for(int i = 0; i < n; i++){
schedules[i][0] = sc.nextInt();
schedules[i][1] = sc.nextInt();
}
System.out.println(maxCount(schedules));
}
public static int maxCount(int[][] schedules) {
Arrays.sort(schedules, (a, b) -> a[1] - b[1]); //二维数组根据结束时间从小到大排序
int count = 0; // 计数
int end = 0; // 当前已经安排的讲座的结束时间
for(int i = 0; i < schedules.length; i++) {
if(end <= schedules[i][0]){ //if (当前班级的开始时间 >= 已经安排的讲座结束时间) {
count++; //安排的讲座数量++;
end = schedules[i][1]; // 时间点往后推
}
}
return count;
}
}
50.随机数排序(相同元素去重问题)
Treeset能够自动排序 + 去重
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
Set<Integer> uniqueNumbers = new TreeSet<>(); //TreeSet能够自动去重 + 自动排序
for(int i = 0; i < n; i++) {
int num = sc.nextInt();
uniqueNumbers.add(num);
}
for(int num : uniqueNumbers) {
System.out.printf("%d ", num);
}
System.out.println();
}
}
双指针
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] nums = new int[n];
for(int i = 0; i < n; i++) {
nums[i] = sc.nextInt();
}
Arrays.sort(nums);
//双指针解决
int l = 0;
int r = 1;
//要求删除重复元素,实际上就是将不重复的元素移到数组的左侧。
while(r < n){
if(nums[l] != nums[r]) {
nums[l + 1] = nums[r];
l++;
}
r++;
}
int[] res = new int[l + 1]; // l + 1 为新数组长度
for(int i = 0; i < l + 1; i++) {
res[i] = nums[i];
}
for(int num : res){
System.out.printf("%d ", num);
}
System.out.println();
}
}
52.携带研究材料||(完全背包问问题)
一维DP
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int M = sc.nextInt();
int V = sc.nextInt();
int[] weights = new int[M];
int[] values = new int[M];
for(int i = 0; i < M; i++) {
weights[i] = sc.nextInt();
values[i] = sc.nextInt();
}
int[] dp = new int[V + 1];
for(int i = 0; i < M; i++) { //先遍历物品
for(int j = weights[i]; j <= V; j++) { //再遍历背包
dp[j] = Math.max(dp[j], dp[j - weights[i]] + values[i]);
}
}
System.out.println(dp[V]);
}
}
54.替换数字(字符串替换问题)
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
System.out.println(replaceNumber(s));
}
public static String replaceNumber(String s) {
int count = 0; //统计数字的个数
int oldSize = s.length(); //原先字符串长度
for(int i = 0; i < oldSize; i++) {
if(Character.isDigit(s.charAt(i))) {
count++;
}
}
char[] newCharArray = new char[oldSize + count * 5];
int newSize = newCharArray.length; //目标字符串长度
System.arraycopy(s.toCharArray(), 0, newCharArray, 0, oldSize);
for(int i = newSize - 1, j = oldSize - 1; j < i; j--, i--) { //从后往前遍历
if(!Character.isDigit(newCharArray[j])) { //不是数字
newCharArray[i] = newCharArray[j];
}else{
newCharArray[i] = 'r';
newCharArray[i - 1] = 'e';
newCharArray[i - 2] = 'b';
newCharArray[i - 3] = 'm';
newCharArray[i - 4] = 'u';
newCharArray[i - 5] = 'n';
i -= 5;
}
}
return new String(newCharArray);
}
}
56.携带矿石资源(多重背包问题)
import java.util.*;
public class Main{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int V = sc.nextInt();
int M = sc.nextInt();
int[] weights = new int[M];
int[] values = new int[M];
int[] counts = new int[M];
for(int i = 0; i < M; i++) {
weights[i] = sc.nextInt();
}
for(int i = 0; i < M; i++) {
values[i] = sc.nextInt();
}
for(int i = 0; i < M; i++) {
counts[i] = sc.nextInt();
}
int[] dp = new int[V + 1];
for(int i = 0; i < weights.length; i++) {
for(int j = V; j >= weights[i]; j--) {
//多重背包就是 01背包 + 遍历个数
for(int k = 1; k <= counts[i] && (j - k * weights[i]) >= 0; k++) {
dp[j] = Math.max(dp[j], dp[j - k * weights[i]] + k * values[i]);
}
}
}
System.out.println(dp[V]);
}
}
57.爬楼梯(m个台阶,完全背包问题 )
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
System.out.println(climbStairs(n, m));
}
public static int climbStairs(int n, int m) {
int[] dp = new int[n + 1];
dp[0] = 1;
for(int i = 1; i <= n; i++) { //完全背包问题
for(int j = 1; j <= m; j++) {
if(i - j >= 0) {
dp[i] += dp[i - j];
}
}
}
return dp[n];
}
}
58.区间和(前缀和数组降低复杂度)
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int size = scanner.nextInt();
int[] numberArray = new int[size];
for (int i = 0; i < size; i++) {
numberArray[i] = scanner.nextInt();
}
// 计算前缀和数组
int[] prefixArray = new int[size];
prefixArray[0] = numberArray[0];
for (int i = 1; i < numberArray.length; i++) {
prefixArray[i] = prefixArray[i - 1] + numberArray[i];
}
// 计算区间和
while (scanner.hasNextInt()) {
int start = scanner.nextInt();
int end = scanner.nextInt();
System.out.println(calculateRangeSum(prefixArray, start, end));
// System.out.println(calculateRangeSum2(numberArray, start, end));
}
}
// 使用前缀和的方法(用end下标对应的数组元素和 - 区间外的元素和 = 区间元素和)
public static int calculateRangeSum(int[] array, int start, int end) {
int prefixSumStart = (start > 0) ? array[start - 1] : 0;
return array[end] - prefixSumStart;
}
}
超时写法
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
for(int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}
while(sc.hasNextInt()) {
int sum = 0;
int a = sc.nextInt();
int b = sc.nextInt();
for(int i = a; i <= b; i++) {
sum += arr[i];
}
System.out.println(sum);
}
}
}
60.匹配前缀的词典(Trie、字典树、前缀树)
import java.util.Scanner;
// 字典树的节点
class TrieNode {
boolean isWord;
final TrieNode[] children;
public TrieNode() {
isWord = false;
children = new TrieNode[26];
}
}
// 字典树
class Trie {
TrieNode root;
Trie() {
root = new TrieNode();
}
public void insert(String word) {
TrieNode current = root;
for (int i = 0; i < word.length(); i++) {
int c = word.charAt(i) - 'a';
if (current.children[c] == null) {
current.children[c] = new TrieNode();
}
current = current.children[c];
}
current.isWord = true;
}
// 完整的字段树应该包含搜索功能,但是此处用不到
// public boolean search(String word) {
// TrieNode current = root;
// for (int i = 0; i < word.length(); i++) {
// int c = word.charAt(i) - 'a';
// if (current.children[c] == null) {
// return false;
// }
// current = current.children[c];
// }
// return current.isWord;
// }
public boolean startWith(String word) {
TrieNode current = root;
for (int i = 0; i < word.length(); i++) {
int c = word.charAt(i) - 'a';
if (current.children[c] == null) {
return false;
}
current = current.children[c];
}
return true;
}
}
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int m = scanner.nextInt();
int n = scanner.nextInt();
scanner.nextLine(); // 消耗掉换行符
Trie trie = new Trie();
for (int i = 0; i < m; i++) {
trie.insert(scanner.nextLine());
}
for (int i = 0; i < n; i++) {
boolean isStart = trie.startWith(scanner.nextLine());
System.out.println(isStart);
}
}
}
62.平方差
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int L = sc.nextInt();
int R = sc.nextInt();
int ans = 0;
for(;L <= R; L++) {
//x = y^2 - z^2 = (y-z)(y+z) x和y-z和y+z奇偶相同
//当x为奇数,都可以满足
//当x为偶数,需要是4的倍数
if(L % 2 != 0 || L % 4 == 0){
ans++;
}
}
System.out.println(ans);
}
}
63.更小的数(动态规划)
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String s = sc.nextLine();
boolean[][] dp = new boolean[s.length()][s.length()];
//初始化
for(int i = 0; i < s.length(); i++) {
for(int j = 0; j <= i; j++) { //j <= i 则 false:初始化题解之外的情况
dp[i][j] = false; //i,j 表示子串区间
}
}
int res = 0;
//
for(int i = s.length() - 2; i >= 0; i--) { //从后往前遍历
for(int j = i + 1; j < s.length(); j++) {
if(s.charAt(i) == s.charAt(j)) dp[i][j] = dp[i + 1][j - 1];
//记住它,可以带入示例,i,j :0 5 与 1 4 都为true
if(s.charAt(i) > s.charAt(j)) dp[i][j] = true;
if(s.charAt(i) < s.charAt(j)) dp[i][j] = false;
if(dp[i][j] == true) res++;
}
}
System.out.println(res);
}
}
65.买瓜(01背包 + 回溯)
import java.util.*;
public class Main{
static int sum = Integer.MAX_VALUE; //刀数
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int target = sc.nextInt();
int total = 0;
int weight;
double[] weights = new double[n * 2];
for(int i = 0; i < n; i++) {
weight = sc.nextInt();
weights[i] = weight;
}
if(total < target) dfs(weights, target, 0, 0, 0);
if(total == target) sum = 0;
System.out.println(sum == Integer.MAX_VALUE ? -1 : sum);
}
//这次的n表示刀数
public static void dfs(double[] weights, int target, double now_weight, int n, int index) {
if(now_weight > target) {
return;
}
if(now_weight == target) {
sum = Math.min(sum, n);
return;
}
for(int i = index; i < weights.length; i++) {
dfs(weights, target, now_weight + weights[i], n, i + 1);
dfs(weights, target, now_weight + weights[i] * 0.5, n + 1, i + 1);
}
}
}
67.异或和之和
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr = new int[n];
int res = 0;
for(int i = 0; i < n; i++) {
arr[i] = sc.nextInt();
}
for(int i = 0; i < n; i++) {
int segment_sum = 0; //子段异或和
for(int j = i; j < n; j++) {
segment_sum ^= arr[j];
res += segment_sum;
}
}
System.out.println(res);
}
}
68.像素放置(扫雷问题)
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int m = sc.nextInt();
sc.nextLine();
int[][] g = new int[n][m];
for(int i = 0; i < n; i++) {
String line = sc.nextLine();
for(int j = 0; j < m; j++) {
char c = line.charAt(j);
if(c == '_') continue;
g[i][j] = c - '0';
}
}
int[] ans = new int[n];
dfs(ans, g, n - 1);
for(int x : ans) {
printInt(x, m);
}
}
static void printInt(int n, int m) {
StringBuilder s = new StringBuilder();
for(int i = 0; i < m; i++) {
s.append(n >> i & 1); //将n右移i位和1 进行与运算
}
System.out.println(s);
}
static boolean dfs(int[] ans, int[][] g, int i) {
int n = g.length, m = g[0].length;
// 所有行的状态都枚举完毕且符合,返回true
if (i < 0) return true;
// 每一行都重新从0开始枚举状态
for (ans[i] = 0; ans[i] < (1 << m); ans[i]++) {
// 如果当前状态满足条件,则往上一行继续枚举
if (check(ans, g, i) && dfs(ans, g, i - 1)) {
return true;
}
}
// 如果无法得到正确的排列,需要将此行重置,防止后续dfs时cnt计算错误
ans[i] = 0;
return false;
}
// 当枚举到第i行时,需要先校验第i行和第i+1行的数字是否满足
static boolean check(int[] ans, int[][] g, int i) {
int n = g.length, m = g[0].length;
for (int j = 0; j < m; j++) {
// 第i行
if (g[i][j] > 0) {
int c = cnt(ans, i, j);
// 第0行特判,因为没有补救机会了,只能判断是否相等
if (i == 0 && c != g[i][j]) return false;
// 由于还有第i-1行没有枚举,所以第i行只判断是否大于(如果小于的话,在i-1行还有补救机会)
if (i > 0 && c > g[i][j]) return false;
}
// 第i+1行
if (i + 1 < n && g[i + 1][j] > 0) {
int c = cnt(ans, i + 1, j);
// 第i+1行的数字已经可以完整判断了,因为i,i+1,i+2这三行都已经枚举了状态
if (c != g[i + 1][j]) return false;
}
}
return true;
}
// 获取第i行第j列周围数字
static int cnt(int[] ans, int i, int j) {
int cnt = 0;
for (int r = i - 1; r <= i + 1; r++) {
if (r < 0 || r >= ans.length) continue;
for (int c = j - 1; c <= j + 1; c++) {
if (c < 0) continue;
cnt += ans[r] >> c & 1;
}
}
return cnt;
}
}
70.冶炼金属
import java.util.*;
public class Main{
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int[] arr1 = new int[n];
int[] arr2 = new int[n];
for(int i = 0; i < n; i++) {
int x = sc.nextInt();
int y = sc.nextInt();
arr1[i] = x / y;
arr2[i] = x / (y + 1);
}
int max = Integer.MAX_VALUE;
int min = Integer.MIN_VALUE;
for(int i : arr2) {
min = Math.max(min, i + 1);
}
System.out.print(min + " ");
for(int i : arr1) {
max = Math.min(max, i);
}
System.out.println(max);
}
}