1. 求解一元三次方程问题
【问题描述】 有形如 ax3+bx2+cx+d=0 这样的一个一元三次方程。给出该方程中各项的系数(a,b,c,d 均为实数),并约定该方程存在三个不同实根(根的范围在-100至100之间),且根与根之差的绝对值>=1。求三个实根,并精确到小数点后两位。
【输入描述】包含4个实数a、b、c、d。
【输出描述】从小到大的3个实根。
【输入样例】1 -5 -4 20
【输出样例】-2.00 2.00 5.00
public class Solution1 {
/**
*【解法一:暴力法】
* 每次i+=0.01,然后带入方程看看是否成立
* 值得注意的是,double运算存在精度丢失,因此方程成立条件不是正好==0,而是<0.0000001
*/
private String[] solveRquation(double a, double b, double c, double d) {
String[] res = new String[3];
int index = 0;
for (double i = -100; i <= 100; i += 0.01) {
if (Math.abs(a * i * i * i + b * i * i + c * i + d) < 0.0000001) {
res[index++] = String.format("%.2f", i);
}
if (index == 3) {
break;
}
}
return res;
}
/**
*【解法二:二分法】
* 题目中有一个极其重要的条件:两个根的绝对值之差>=1
* 我们可以这样:每次i+=1,然后在[i, i+0.99]这个区间内二分出根,上面的条件保证了这个区间内最多1个根
* 另外,在二分减治时,每次收缩0.01(left=mid+1, right=m)。
*/
private String[] solveRquation2(double a, double b, double c, double d) {
String[] res = new String[3];
int index = 0;
for (double i = -100; i <= 100; i++) {
double l = i;
double r = i + 0.99;
if ((a * l * l * l + b * l * l + c * l + d) * (a * r * r * r + b * r * r + c * r + d) > 0) {
continue;
} else {
while (l < r) {
double m = l + (r - l) / 2.0;
if ((a * l * l * l + b * l * l + c * l + d) * (a * m * m * m + b * m * m + c * m + d) > 0) {
l = m + 0.01;
} else {
r = m;
}
}
res[index++] = String.format("%.2f", l);
}
if (index == 3) {
break;
}
}
return res;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
double a = scanner.nextDouble();
double b = scanner.nextDouble();
double c = scanner.nextDouble();
double d = scanner.nextDouble();
String[] res = new Solution1().solveRquation2(a, b, c, d);
System.out.println(Arrays.toString(res));
}
}
2. 求解完数问题
【问题描述】 完数的定义:如果一个大于1的正整数的所有真因子(不包括自己的那些因子)之和等于它的本身,则称这个数是完数,比如6,28都是完数:6=1+2+3;28=1+2+4+7+14。本题的任务是判断两个正整数之间完数的个数。
【输入描述】第一行是一个正整数n,表示测试实例的个数,然后就是n个测试实例,每个实例占一行,由两个正整数num1和num2组成,(1<num1,num2<10000) 。
【输出描述】对于每组测试数据,请输出num1和num2之间(包括num1和num2)存在的完数个数。
【输入样例】2
2 5
5 7
【输出样例】0
1
public class Solution2 {
/**
*【解法一:暴力法】
* 根据定义直接解出此题
* 多说一句,因子的定义是"所有可以整除这个数的数,但不包括这个数自身",即"包含1,但不包含自身"
* 再多说一句,题目中没说mum1<=num2,但这种文字游戏实在没有必要,因此未予以处理
*/
private int countPerfectNumber(int min, int max) {
int cnt = 0;
for (int n = min; n <= max; n++) {
if (isPerfectNumber(n)) {
cnt++;
System.out.println(n);
}
}
return cnt;
}
private boolean isPerfectNumber(int n) {
int sum = 0;
for (int i = 1; i < n; i++) {
if (n % i == 0) {
sum += i;
}
}
return sum == n;
}
/**
*【解法二:伪暴力法】
* 审题可知输入的范围是1<num1<num2<10000,这个范围内最多也就4个完全数———6、28、496、8182
*/
private int countPerfectNumber2(int min, int max) {
int cnt = 0;
if (min <= 6 && max >= 6) {
cnt++;
}
if (min <= 28 && max >= 28) {
cnt++;
}
if (min <= 496 && max >= 496) {
cnt++;
}
if (min <= 8128 && max >= 8128) {
cnt++;
}
return cnt;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int len = scanner.nextInt();
int[] res = new int[len];
int index = 0;
Solution2 solution2 = new Solution2();
for (int i = 0; i < len; i++) {
int min = scanner.nextInt();
int max = scanner.nextInt();
res[index++] = solution2.countPerfectNumber2(min, max);
}
for (int n : res) {
System.out.println(n);
}
}
}
3. 求解好多鱼问题
【问题描述】 牛牛有一个鱼缸。鱼缸里面已经有n条鱼,每条鱼的大小为fishSize[i] (1 ≤ i ≤ n,均为正整数),牛牛现在想把新捕捉的鱼放入鱼缸。鱼缸内存在着大鱼吃小鱼的定律。经过观察,牛牛发现一条鱼A的大小为另外一条鱼B大小的2倍到10倍(包括2倍大小和10倍大小),鱼A会吃掉鱼B。考虑到这个,牛牛要放入的鱼就需要保证:
1、放进去的鱼是安全的,不会被其他鱼吃掉
2、这条鱼放进去也不能吃掉其他鱼
鱼缸里面已经存在的鱼已经相处了很久,不考虑他们互相捕食。现在知道新放入鱼的大小范围minSize,maxSize(考虑鱼的大小都是整数表示),牛牛想知道有多少种大小的鱼可以放入这个鱼缸。
【输入描述】输入数据包括3行。 第一行为新放入鱼的尺寸范围minSize,maxSize(1 ≤ minSize,maxSize ≤ 1000),以空格分隔。 第二行为鱼缸里面已经有鱼的数量n(1 ≤ n ≤ 50) 第三行为已经有的鱼的大小fishSize[i](1≤ fishSize[i] ≤ 1000),以空格分隔。
【输出描述】输出有多少种大小的鱼可以放入这个鱼缸。考虑鱼的大小都是整数表示。
【输入样例】1 12
1
1
【输出样例】3
public class Solution3 {
/**
*【解法一:暴力法】
* 第一层for循环是[min,max]中每一种鱼的大小,第二层for循环是鱼缸里已有的每一条鱼,判断是否有大鱼吃小鱼的情况
* 代码略
*/
/**
*【解法二:数组法】
* 不妨开一个大小为1001的数组,记录每种大小的鱼是否合法;鱼缸里已有的每一条鱼,从吃与被吃的角度修改这个数组
* 思路不难,代码不难,但有两个细节是难点:
* 细节1:
* 假设鱼缸里有一条大小为21的鱼,那么哪个范围内的鱼会被它吃呢?答案是[2.1, 10.5],如何处理取整的问题呢?
* 下限是向上取整,上限是向下取整(2不会被吃,10会被吃),最终答案是[3, 10]
* 细节2:
* 向下取整直接就是java默认的地板除,但如何向上取整呢?
* 可以用这个技巧:Math.ceil(n/10.0) == (n+9)/10
*/
private int cntSpeciesOfFish(int[] fishes, int min, int max) {
boolean[] illegalSize = new boolean[1001];
for (int fish : fishes) {
for (int i = fish * 2; i <= max && i <= fish * 10; i++) {
illegalSize[i] = true;
}
for (int i = fish / 2; i >= min && i >= (fish + 9) / 10; i--) {
illegalSize[i] = true;
}
}
int cnt = 0;
for (int i = min; i <= max; i++) {
cnt += illegalSize[i] ? 0 : 1;
}
return cnt;
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int min = scanner.nextInt();
int max = scanner.nextInt();
int len = scanner.nextInt();
int[] fishes = new int[len];
for (int i = 0; i < len; i++) {
fishes[i] = scanner.nextInt();
}
System.out.println(new Solution3().cntSpeciesOfFish(fishes, min, max));
}
}
4. 求解推箱子问题
【问题描述】推箱子是一款风靡全球的益智小游戏,玩家需要将箱子推到仓库中的目标位置。
游戏地图用大小为 n * m 的网格 grid 表示,其中每个元素可以是墙、地板或者是箱子。
现在你将作为玩家参与游戏,按规则将箱子 ‘B’ 移动到目标位置 ‘T’ :
玩家用字符 ‘X’ 表示,只要他在地板上,就可以在网格中向上、下、左、右四个方向移动。
地板用字符 ‘.’ 表示,意味着可以自由行走。
墙用字符 ‘#’ 表示,意味着障碍物,不能通行。
箱子仅有一个,用字符 ‘*’ 表示。相应地,网格上有一个目标位置 ‘@’。
玩家需要站在箱子旁边,然后沿着箱子的方向进行移动,此时箱子会被移动到相邻的地板单元格。记作一次「推动」。
玩家无法越过箱子。
返回将箱子推到目标位置的最小推动次数,如果无法做到,请返回 -1。
public class Solution4{
/**
*【BFS+DFS】
* 以箱子的视角进行BFS
* 以人的视角进行DFS
* 后者作为前者得以进行的前提
*/
public int minPushBox(char[][] grid) {
int m = grid.length;
int n = grid[0].length;
// 遍历一次,找出箱子起点/终点,人的初始位置
int startX = -1;
int startY = -1;
int targetX = -1;
int targetY = -1;
int personX = -1;
int personY = -1;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (grid[i][j] == '*') {
startX = i;
startY = j;
}
if (grid[i][j] == '@') {
targetX = i;
targetY = j;
grid[i][j] = '.';
}
if (grid[i][j] == 'X') {
personX = i;
personY = j;
grid[i][j] = '.';
}
}
}
// 初始化队列,加入元素以启动BFS
boolean[][][] visited = new boolean[m][n][4];
Queue<Box> queue = new LinkedList<>();
for (int i = 0; i < 4; i++) {
int[] direction = directions[i];
if (personCanReach(grid, m, n, personX, personY, startX - direction[0], startY - direction[1], new boolean[m][n])) {
queue.add(new Box(startX, startY, i));
visited[startX][startY][i] = true;
}
}
// 以箱子的视角开始BFS
int step = 0;
while (!queue.isEmpty()) {
int size = queue.size();
while (size-- > 0) {
Box box = queue.poll();
grid[box.x][box.y] = '*';
personX = box.x - directions[box.from][0];
personY = box.y - directions[box.from][1];
if (box.x == targetX && box.y == targetY) {
return step;
}
for (int i = 0; i < 4; i++) {
int[] direction = directions[i];
int nextX = box.x + direction[0];
int nextY = box.y + direction[1];
// 人是否能绕到箱子的后面?
if (!personCanReach(grid, m, n, personX, personY, box.x - direction[0], box.y - direction[1], new boolean[m][n])) {
continue;
}
// 箱子的下个位置是否合法?
if (!isValid(grid, m, n, nextX, nextY)) {
continue;
}
// 箱子的下一个状态是不是重复了?
if (visited[nextX][nextY][i]) {
continue;
}
queue.add(new Box(nextX, nextY, i));
visited[nextX][nextY][i] = true;
}
grid[box.x][box.y] = '.';
}
step++;
}
return -1;
}
// 其含义是从【上】【下】【左】【右】
private final static int[][] directions = {{-1, 0}, {1, 0}, {0, -1}, {0, 1}};
// 静态内部类是个顶级类,可当成外部类来看
private static class Box {
int x;
int y;
int from;
public Box(int x, int y, int from) {
this.x = x;
this.y = y;
this.from = from;
}
}
// 人是否可以某一位置(startX, startY)到达另一位置(targetX, targetY)
private boolean personCanReach(char[][] grid, int m, int n, int startX, int startY, int targetX, int targetY, boolean[][] visited) {
if (startX == targetX && startY == targetY) {
return true;
}
visited[startX][startY] = true;
for (int[] direction : directions) {
int nextX = startX + direction[0];
int nextY = startY + direction[1];
if (isValid(grid, m, n, nextX, nextY) && !visited[nextX][nextY]) {
if (personCanReach(grid, m, n, nextX, nextY, targetX, targetY, visited)) {
return true;
}
}
}
return false;
}
// 某位置是否可以踏足
private boolean isValid(char[][] grid, int m, int n, int x, int y) {
return x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == '.';
}
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
List<Integer> res = new ArrayList<>();
while (true) {
int len1 = scanner.nextInt();
if (len1 == 0) {
break;
}
int len2 = scanner.nextInt();
char[][] grid = new char[len1][len2];
for (int i = 0; i < len1; i++) {
for (int j = 0; j < len2; j++) {
grid[i][j] = scanner.next().charAt(0);
}
}
res.add(new Solution4().minPushBox(grid));
}
for (int n : res) {
System.out.println(n);
}
}
}
⭐️ 这一定是全网最优美的Java解法 >_<