金币
题目链接:https://www.lanqiao.cn/problems/357/learning/
这个题直接简单的模拟就ok了,外层循环更新每天发的硬币数量,里面统计这些硬币发几天,其实内层的for循环可以换成一个乘法提升速度,但是这个题数据量比较小也就没有必要了,懒得改了hhh
package daily;
import java.util.Scanner;
/**
* https://www.lanqiao.cn/problems/357/learning/
*
* @author Jia
*
*/
public class day3_23_1 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int K = sc.nextInt();
sc.close();
long ans = 0;// 答案
int day = 0;// 天数
int i = 1;// 这天发几个硬币
while (day < K) {
for (int j = 0; j < i; j++) {
ans += i;
day++;
if (day >= K) {
break;
}
}
i++;
}
System.out.println(ans);
}
}
优秀的拆分
题目链接:https://www.lanqiao.cn/problems/801/learning/
这个题相对来说也比较常规,首先先构造小于输入整数n的所有2的幂集(
init
函数),然后使用回溯法的子集树模板去寻找所有可以满足合为n的组合,找到后直接输出结果,不要被这个名字吓到,回溯法其实就是高级一点的爆搜,只不过是在爆搜的时候加上了一些约束和限制,让搜索的情况少了很多。对于回溯法的子集树模板不熟悉的可以去看昨天的博客算式900中写到的代码框架,子集树和排列树最好都直接背过,如果对于这些还是不理解的话建议在这个网站上把他的回溯法的题再做做,我当时上课只是听了理论知识,下来还是不会写,然后在这里刷了点题(也不能说点,实际上是全刷了一遍)然后掌握了使用方法。
package daily;
import java.util.ArrayList;
import java.util.Scanner;
/**
* https://www.lanqiao.cn/problems/801/learning/
*
* @author Jia
*
*/
public class day3_23_2 {
static ArrayList<Integer> ans = new ArrayList<>();
static int sum = 0;
static boolean find = false;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
sc.close();
ArrayList<Integer> list = init(n);// 获得小于输入整数n的所有2的次幂
backtrack(list, list.size() - 1, n);
if (find == false) {
// 当没找到时输出-1
System.out.println(-1);
}
}
/**
* 回溯法寻找子集树
*
* @param list
* @param index
* @param target
*/
private static void backtrack(ArrayList<Integer> list, int index, int target) {
if (sum == target) {
// 当找到一种组合时直接打印出结果,并且设置当前状态为已经找到,不再遍历后面的分支
find = true;
for (int i = 0; i < ans.size(); i++) {
System.out.print(ans.get(i) + " ");
}
} else {
for (int i = index; i >= 0; i--) {
if (sum + list.get(i) <= target && !find) {
ans.add(list.get(i));
sum += list.get(i);
backtrack(list, i - 1, target);
sum -= ans.remove(ans.size() - 1);
}
}
}
}
/**
* 获得小于输入整数n的所有2的次幂
*
* @param n
* @return
*/
private static ArrayList<Integer> init(int n) {
ArrayList<Integer> list = new ArrayList<>();
int i = 2;
while (i <= n) {
list.add(i);
i = i * 2;
}
return list;
}
}
穿越雷区
题目链接:http://lx.lanqiao.cn/problem.page?gpid=T2818
这个题也是一个很简单的bfs遍历搜答案就行了,昨天也有一个类似的走迷宫,刚开始的几天也有一个迷宫也都是bfs,基本都是套模板
这个题稍微限制多了一点就是必须交叉这走 + 和 - ,我直接在
constrain
函数里面判断了,这里一般建议都是写到一个函数里面,这样原代码里面就看起来比较简单了,不然太乱然后我还碰到了一点问题是我开始没有用set去重,导致最后直接超时了,后面加set去重时又因为是自己定义的类,调用的是顶级类
Object
的equals和hashCode方法导致set根本没发挥作用,然后我还不会重写这个,最后看的这篇博客才把set的去重写好,建议对set的去重原理不熟悉的看看这个文章这类型的bfs实际上都是一样,还是建议大家把这三个题都搞懂,实在不行把代码考到本地,用题目的测试用例自己跟着代码手动debug一边,基本那就都懂了,然后把模板背过直接用就好了
package daily;
import java.util.Deque;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Scanner;
import java.util.Set;
/**
* http://lx.lanqiao.cn/problem.page?gpid=T2818
*
* @author Jia
*
*/
public class day3_23_3 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
char[][] zone = new char[n][n];
ZoneNode A = new ZoneNode();
ZoneNode B = new ZoneNode();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
zone[i][j] = sc.next().charAt(0);
if (zone[i][j] == 'A') {
A.set(i, j, 'A');
} else if (zone[i][j] == 'B') {
B.set(i, j, 'B');
}
}
}
sc.close();
// 参数初始化
Deque<ZoneNode> queue = new LinkedList<>();
queue.addFirst(A);
Set<ZoneNode> set = new HashSet<>();
set.add(A);
int[] nextRow = { 0, -1, 0, 1 };
int[] nextCol = { 1, 0, -1, 0 };
boolean flag = false;
int ans = 0;
// bfs遍历
while (!queue.isEmpty()) {
int size = queue.size();
ans++;
while (size > 0) {
ZoneNode node = queue.removeLast();
size--;
// 四个方向看看
for (int i = 0; i < nextRow.length; i++) {
int newRow = node.row + nextRow[i];
int newCol = node.col + nextCol[i];
if (bound(n, newRow, newCol)) {
if (zone[newRow][newCol] == 'B') {
flag = true;
break;
}
ZoneNode newNode = new ZoneNode(newRow, newCol, zone[newRow][newCol]);
if (constrain(set, node, newNode)) {
queue.addFirst(newNode);
set.add(newNode);// 去重
}
}
}
if (flag) {
break;
}
}
if (flag) {
break;
}
}
System.out.println(ans);
}
/**
* 是否在题目给出的区域范围中
*
* @param n
* @param newRow
* @param newCol
* @return
*/
private static boolean bound(int n, int newRow, int newCol) {
return newRow >= 0 && newRow < n && newCol >= 0 && newCol < n;
}
/**
* 判断下一个格子能不能走
*
* 1.这个格子没有走过(所有都必须满足)
*
* 2.当前格子如果是A的话,则下一个格子可以走
*
* 3.当前格子是 + ,则下一个格子是 - 才可以走,反之亦然
*
* @param set
* @param node
* @param newNode
* @return
*/
private static boolean constrain(Set<ZoneNode> set, ZoneNode node, ZoneNode newNode) {
return !set.contains(newNode) && (node.symbol == 'A' || (node.symbol == '-' && newNode.symbol == '+')
|| (node.symbol == '+' && newNode.symbol == '-'));
}
}
class ZoneNode {
int row;
int col;
char symbol;
@Override
public boolean equals(Object obj) {
ZoneNode otherNode = (ZoneNode) obj;
return row == otherNode.row && col == otherNode.col;
}
@Override
public int hashCode() {
return row * 101 + col * 102;
}
public ZoneNode() {
super();
}
public ZoneNode(int row, int col, char symbol) {
super();
this.row = row;
this.col = col;
this.symbol = symbol;
}
public void set(int row, int col, char symbol) {
this.row = row;
this.col = col;
this.symbol = symbol;
}
}
蓝肽子序列
题目链接:https://www.lanqiao.cn/problems/1030/learning/
什么扯淡的蓝肽,实际就是求最长公共子序列呗,绝了,看题半天看不懂,看到最后两行才知道干啥
对于求最长公共子序列就是一个动态规划问题了,不知道有没有hxd去看了我前几天发的那个代码随想录的里面的dp讲解,里面有一个基本一模一样的题,按照他说的五步走基本就可以拿下大部分的dp问题了。如果你对动态规划还是不熟悉的话建议跟着它的顺序把动态规划话几天或者一周的时间刷一下,不会的看看他的题解,完了基本上动态规划大部分题你也就会了。
这个题建议去看看他的题解,与本题唯一不同的是他是找两个字符串的公共子序列,我们这个经过处理后变成了找两个数组的公共子序列,实质都是一样的。
建议先按照这个递推公式去手动推一下下面那个例子,这样你对于这种二维dp的基本也就能理解他是怎样状态转移的。
package daily;
import java.util.ArrayList;
import java.util.Scanner;
/**
* https://www.lanqiao.cn/problems/1030/learning/
*
* @author Jia
*
*/
public class day3_23_4 {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
String str1 = sc.nextLine();
String str2 = sc.nextLine();
sc.close();
ArrayList<String> arr1 = splitString(str1);
ArrayList<String> arr2 = splitString(str2);
// 动态规划求最优解
int[][] dp = new int[arr1.size() + 1][arr2.size() + 1];
for (int i = 1; i < dp.length; i++) {
String subStr1 = arr1.get(i - 1);
for (int j = 1; j < dp[0].length; j++) {
String subStr2 = arr2.get(j - 1);
if (subStr1.equals(subStr2)) {
// 当前指向的两个元素相等,则直接是出去这两个元素的子序列的最大长度+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.println(dp[arr1.size()][arr2.size()]);
}
/**
* 将字符串拆分成单词的形式
*
* @param str
* @return
*/
private static ArrayList<String> splitString(String str) {
ArrayList<String> list = new ArrayList<>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < str.length(); i++) {
if (Character.isUpperCase(str.charAt(i))) {
// 如果是大写的则需要将当前创建好的字符串加入list,然后清空StringBilder
if (sb.length() != 0) {
list.add(sb.toString());
sb = new StringBuilder();
}
}
sb.append(str.charAt(i));
}
list.add(sb.toString());
return list;
}
}