试题 A: 阶乘求和
[题解]
由于从 40 ! 40! 40! 开始末尾 9 位都为 0,即我们只要关注前 39 个阶乘相加即可。
public class Main {
static final long MOD = (int) 1e9;
public static void main(String[] args) {
long ans = 0;
long num = 1;
for (int i = 1; i < 40; i++) {
num = (num * i) % MOD;
ans = (ans + num) % MOD;
}
System.out.println(ans);
}
}
[答案]
420940313
试题 B: 幸运数字
[题解]
public class Main {
public static void main(String[] args) {
int ans = 1;
for (int i = 0; ; ans++) {
String binaryString = Integer.toBinaryString(ans);
String octalString = Integer.toOctalString(ans);
String decString = Integer.toString(ans);
String hexString = Integer.toHexString(ans);
int binSum = calc(binaryString);
int octSum = calc(octalString);
int decSum = calc(decString);
int hexSum = calc(hexString);
if (ans % binSum == 0 && ans % octSum == 0 && ans % decSum == 0 && ans % hexSum == 0) i++;
if (i == 2023) {
System.out.println(ans);
break;
}
}
}
private static int calc(String s) {
int res = 0;
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) >= '0' && s.charAt(i) <= '9') {
res += s.charAt(i) - '0';
} else if (s.charAt(i) >= 'a' && s.charAt(i) <= 'f') {
res += s.charAt(i) - 'a' + 10;
}
}
return res;
}
}
[答案]
215040
试题 C: 数组分割
[题解]
要使得两个集合都为偶数且原集合所有元素都要分完,则需要若干个偶数与偶数个奇数,出现奇数个奇数,则一定无法实现题目要求。
当其中一个集合的元素确定,另一个集合也随之确定,即我们只要考虑一个集合有多少种可能性。
将两个奇数看为一个整体,我们能得到集合所有可能为 2 e v e n + o d d − 1 2^{even + odd - 1} 2even+odd−1, 需要特判没有奇数的情况,防止数值错误,即正确的为 2 e v e n 2^{even} 2even。
import java.util.*;
/**
* 2023 分割数组 排列组合 集合 快速幂
*
*/
public class Main {
static int MOD = (int) 1e9 + 7;
public static void main(String[] args) {
new Main().go();
}
void go() {
Scanner input = new Scanner(System.in);
int n = input.nextInt();
long ans = 0;
for(int i = 0; i < n; i++) {
int m = input.nextInt(), odd = 0, even = 0;
for(int j = 0; j < m; j++) {
if (input.nextInt() % 2 == 0) even++;
else odd++;
}
if (odd % 2 == 0) ans = fastPow(2, even) * fastPow(2, odd == 0 ? 0 : odd - 1) % MOD;
System.out.println(ans);
ans = 0;
}
}
long fastPow(long a, long b) {
if (b == -1) return 1;
long res = 1;
while(b > 0) {
if((b & 1) == 1) res = res *a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res % MOD;
}
}
试题 D: 矩形总面积
[题解]
矩形总面积 = 两个矩形面积之和 - 重叠部分矩形面积
两个矩形面积很好算,我们只要确定重叠部分矩形即可。
通过归纳可以发现重叠部分矩形左下角坐标 ( m a x ( x 1 , x 3 ) , m a x ( y 1 , y 3 ) ) (max(x_1, x_3), max(y_1,y_3)) (max(x1,x3),max(y1,y3)), 右上角坐标 ( m i n ( x 2 , x 4 ) , m i n ( y 2 , y 4 ) ) (min(x_2, x_4), min(y_2,y_4)) (min(x2,x4),min(y2,y4)) 。
当左下角坐标在右上角坐标右边时,代表两个矩形不重叠,即重叠部分矩形面积为零。
import java.io.*;
/**
* 2023 矩形总面积 数学
*
*/
public class Main {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws Exception{
new Main().go();
}
void go() throws Exception{
String[] s = br.readLine().split(" ");
int x1 = Integer.valueOf(s[0]);
int y1 = Integer.valueOf(s[1]);
int x2 = Integer.valueOf(s[2]);
int y2 = Integer.valueOf(s[3]);
int x3 = Integer.valueOf(s[4]);
int y3 = Integer.valueOf(s[5]);
int x4 = Integer.valueOf(s[6]);
int y4 = Integer.valueOf(s[7]);
long s1 = (long)(x2 - x1) * (long)(y2 - y1);
long s2 = (long)(x4 - x3) * (long)(y4 - y3);
int x_left = Math.max(x1, x3);
int y_left = Math.max(y1, y3);
int x_right = Math.min(x2, x4);
int y_right = Math.min(y2, y4);
int length = x_right - x_left;
int width = y_right - y_left;
long s3 = 0;
if (length > 0 && width > 0) s3 = (long)length * (long)width;
pw.println(s1 + s2 - s3);
pw.flush();
}
}
试题 E: 蜗牛
[题解]
明确蜗牛爬行的起点为 (0, 0), 终点为 (x, 0)。 x 表示最后一根竹竿底部位置
蜗牛每次爬行面临两个选择,①顺着地面;②走传送门
每次选择都要最优,且具备无后效性,可以使用 dp 来做。
定义状态 f(i,j), i 表示在第几根竹竿上; j = 0 表示蜗牛在竹竿底部, j = 1 表示蜗牛在传送门
初始状态 f ( 1 , 0 ) = x f(1,0) = x f(1,0)=x; f ( 1 , 1 ) = x + y 0.7 f(1,1) = x + \frac{y} {0.7} f(1,1)=x+0.7y。 x 表示第一根竹竿的位置,y 表示第一个传送门的位置
f ( i , 0 ) = m i n ( y 1.3 + f ( i − 1 , 1 ) , Δ x + f ( i − 1 , 0 ) ) f(i,0) = min(\frac{y}{1.3} + f(i - 1, 1), \Delta x + f(i - 1, 0)) f(i,0)=min(1.3y+f(i−1,1),Δx+f(i−1,0))
新传送门在传送到位置上方 f ( i , 1 ) = m i n ( f ( i − 1 , 1 ) , Δ x + Δ y 0.7 + f ( i − 1 , 0 ) ) f(i,1) = min(f(i - 1, 1), \Delta x + \frac{\Delta y}{0.7}+ f(i - 1, 0)) f(i,1)=min(f(i−1,1),Δx+0.7Δy+f(i−1,0))
新传送门在传送到位置下方 f ( i , 1 ) = m i n ( f ( i − 1 , 1 ) , Δ x + Δ y 1.3 + f ( i − 1 , 0 ) ) f(i,1) = min(f(i - 1, 1), \Delta x + \frac{\Delta y}{1.3}+ f(i - 1, 0)) f(i,1)=min(f(i−1,1),Δx+1.3Δy+f(i−1,0))
y y y 表示传送门的位置, Δ x \Delta x Δx 表示两竹竿之间的距离, Δ y \Delta y Δy 表示到新传送门距离
为节省空间,代码中的 x1 就是 f ( i , 0 ) f(i,0) f(i,0), x2 就是 f ( i , 1 ) f(i,1) f(i,1), y1 存放变化后的 x1,以防对 x2 计算产生干扰。
import java.io.*;
import java.util.*;
/**
* 2023 蜗牛 线性dp
*
*/
public class Main {
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
PrintWriter pw = new PrintWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws Exception{
new Main().go();
}
void go() throws Exception{
int n = Integer.valueOf(br.readLine());
String[] s = br.readLine().split(" ");
int[] nums = Arrays.stream(s).mapToInt(Integer::parseInt).toArray();
if (n == 1) {
pw.printf("%.2f", (double)(nums[0]));
pw.flush();
return ;
}
int[][] portal = new int[n - 1][];
for(int i = 0; i < n - 1; i++) {
portal[i] = Arrays.stream(br.readLine().split(" ")).mapToInt(Integer::parseInt).toArray();
}
double x1 = nums[0], x2 = nums[0] + portal[0][0] / 0.7;
for(int i = 1; i < n; i++) {
int distance = nums[i] - nums[i - 1];
double y1 = Math.min(x1 + distance, x2 + portal[i - 1][1] / 1.3);
if(i == n - 1) {
pw.printf("%.2f", y1);
pw.flush();
return ;
}
x2 = Math.min((x1 + distance +portal[i][0] / 0.7),
(x2 + (portal[i][0] > portal[i - 1][1] ?
(portal[i][0] - portal[i - 1][1]) / 0.7 :
(portal[i - 1][1] - portal[i][0]) / 1.3)));
x1 = y1;
}
pw.flush();
}
}
试题 F: 合并区域
[题解]
一个区域旋转会出现 4 种情况,我们可以开个三维数组将这四种情况直接记录下来
可直接枚举两个区域所有合并情况,总共会出现 4 * 4 * (2n - 1) 种情况
我们只需找到所有情况中最大的连通面积即可。
import java.io.*;
import java.util.*;
/**
* 2023 合并区域 dfs 剪枝
*/
public class Main {
static BufferedReader br;
static StreamTokenizer st;
static PrintWriter pw;
public static void main(String[] args) throws Exception {
new Main().go();
}
private void go() throws Exception {
br = new BufferedReader(new InputStreamReader(System.in));
st = new StreamTokenizer(br);
pw = new PrintWriter(new OutputStreamWriter(System.out));
getAns();
pw.flush();
}
private int nextInt() throws Exception {
st.nextToken();
return (int) st.nval;
}
static final int N = 55;
// 存放区域长度
int n;
// 存放两块区域
int[][][] matrix1, matrix2;
// 存放合并后的区域
int[][] area = new int[3 * N - 2][2 * N];
int length, width;
int ans;
private void getAns() throws Exception {
n = nextInt();
matrix1 = getMatrix();
matrix2 = getMatrix();
length = 2 * n;
width = 3 * n - 2;
for (int i = 0; i < 4; i++) {
for (int j = 0; j < 4; j++) {
for (int k = 0; k < 2 * n - 1; k++) {
fill(matrix1[i], matrix2[j], k);
find();
}
}
}
pw.println(ans);
}
private void find() {
for (int i = 1; i <= width; i++) {
for (int j = 1; j <= length; j++) {
cnt = 0;
if (vis[i][j] == 0) dfs(i, j);
ans = Math.max(ans, cnt);
}
}
}
int[][] vis = new int[3 * N - 2][2 * N];
int cnt;
private void dfs(int x, int y) {
if (x < 1 || y < 1 || x > width || y > length) {
return;
}
if (area[x][y] == 0 || vis[x][y] == 1) return;
vis[x][y] = 1;
cnt += 1;
dfs(x + 1, y);
dfs(x - 1, y);
dfs(x, y + 1);
dfs(x, y - 1);
}
/**
* @param ints
* @param ints1
* @param offset 偏移量,第二个相对合并区域之后偏移的位置 [0, 2n - 2]
*/
private void fill(int[][] ints, int[][] ints1, int offset) {
for (int i = 1; i <= width; i++) {
for (int j = 1; j <= length; j++) {
area[i][j] = 0;
vis[i][j] = 0;
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
area[n + i - 1][j] = ints[i][j];
area[i + offset][n + j] = ints1[i][j];
}
}
}
private int[][][] getMatrix() throws Exception {
int[][][] matrix = new int[4][N][N];
for (int[][] ints : matrix) {
for (int[] anInt : ints) {
Arrays.fill(anInt, 0);
}
}
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
int num = nextInt();
matrix[0][i][j] = num;
matrix[1][j][n - i + 1] = num;
matrix[2][n - i + 1][n - j + 1] = num;
matrix[3][n - j + 1][i] = num;
}
}
return matrix;
}
}
试题 G: 买二赠一
[题解]
下面几个代码实际区别不大,挑个自己喜欢的即可
写法一:纯数组,直观模拟
import java.io.*;
import java.util.Arrays;
/**
* 2023 买一赠二 模拟
*/
public class Main {
public static void main(String[] args) throws Exception {
new Main().go();
}
static BufferedReader br;
static StreamTokenizer st;
static PrintWriter pw;
private void go() throws Exception {
br = new BufferedReader(new InputStreamReader(System.in));
st = new StreamTokenizer(br);
pw = new PrintWriter(new OutputStreamWriter(System.out));
getAns();
pw.flush();
}
private int nextInt() throws Exception {
st.nextToken();
return (int) st.nval;
}
private long nextLong() throws Exception {
st.nextToken();
return (long) st.nval;
}
static final int N = (int) (5 * 10e5 + 10);
int n;
long[] num = new long[N];
long ans;
private void getAns() throws Exception {
n = nextInt();
for (int i = 1; i <= n; i++) {
num[i] = nextLong();
}
Arrays.sort(num, 1, n + 1);
// 该赠送商品的小标
int idx = n;
for (int i = n; idx > 0 && i > 0; ) {
// 拿走一件商品
// num[i] == 0 表示商品已经免费拿走了
while (i > 0 && num[i] == 0) i--;
i--;
// 拿走另一件商品
while (i > 0 && num[i] == 0) i--;
long price = num[i];
i--;
while (idx > 0 && num[idx] > price / 2) idx--;
if (idx > 0) {
num[idx] = 0;
idx--;
}
}
for (int i = 1; i <= n; i++) {
ans += num[i];
}
pw.println(ans);
}
}
写法二:纯数组
import java.io.*;
import java.util.Arrays;
/**
* 2023 买一赠二 模拟
*/
public class Main {
public static void main(String[] args) throws Exception {
new Main().go();
}
static BufferedReader br;
static StreamTokenizer st;
static PrintWriter pw;
private void go() throws Exception {
br = new BufferedReader(new InputStreamReader(System.in));
st = new StreamTokenizer(br);
pw = new PrintWriter(new OutputStreamWriter(System.out));
getAns();
pw.flush();
}
private int nextInt() throws Exception {
st.nextToken();
return (int) st.nval;
}
private long nextLong() throws Exception {
st.nextToken();
return (long) st.nval;
}
static final int N = (int) (5 * 10e5 + 10);
int n;
long[] num = new long[N];
long ans;
private void getAns() throws Exception {
n = nextInt();
for (int i = 1; i <= n; i++) {
num[i] = nextLong();
}
Arrays.sort(num, 1, n + 1);
// i 表示正常购买下标, j 表示赠送商品下标
int i = n, j = n;
// 购买商品件数
int cnt = 0;
while (i > 0) {
// 表示商品已被赠送
if (num[i] == 0) {
i--;
continue;
}
cnt++;
// 表示可以赠送商品
if (cnt % 2 == 0) {
while (j > 0 && num[j] > num[i] / 2) j--;
if (j > 0) {
num[j] = 0;
j--;
}
}
ans += num[i];
i--;
}
pw.println(ans);
}
}
写法三:数组 + 队列
import java.io.*;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
/**
* 2023 买一赠二 模拟
*/
public class Main {
public static void main(String[] args) throws Exception {
new Main().go();
}
static BufferedReader br;
static StreamTokenizer st;
static PrintWriter pw;
private void go() throws Exception {
br = new BufferedReader(new InputStreamReader(System.in));
st = new StreamTokenizer(br);
pw = new PrintWriter(new OutputStreamWriter(System.out));
getAns();
pw.flush();
}
private int nextInt() throws Exception {
st.nextToken();
return (int) st.nval;
}
private long nextLong() throws Exception {
st.nextToken();
return (long) st.nval;
}
static final int N = (int) (5 * 10e5 + 10);
int n;
long[] num = new long[N];
long ans;
private void getAns() throws Exception {
n = nextInt();
for (int i = 1; i <= n; i++) {
num[i] = nextLong();
}
Arrays.sort(num, 1, n + 1);
// 购买商品件数
int cnt = 0;
// 存放需要应该赠送商品的最大价格
Queue<Long> queue = new LinkedList<>();
for (int i = n; i > 0; i--) {
// 表示商品已被赠送
if (!queue.isEmpty() && queue.peek() >= num[i]) {
queue.poll();
continue;
}
cnt++;
// 表示可以赠送商品
if (cnt % 2 == 0) queue.add(num[i] / 2);
ans += num[i];
}
pw.println(ans);
}
}
试题 H: 合并石子
[题解]
import java.io.*;
import java.util.*;
import java.math.*;
import java.util.stream.*;
/**
* 2023 合并石子 区间DP
* f(i,j,k) 表示 合并 [i, j] 区间颜色为 k 的石子所需花费
*/
public class Main {
public static void main(String[] args) throws Exception {
new Main().go();
}
static BufferedReader br;
static StreamTokenizer st;
static PrintWriter pw;
private void go() throws Exception {
br = new BufferedReader(new InputStreamReader(System.in));
st = new StreamTokenizer(br);
pw = new PrintWriter(new OutputStreamWriter(System.out));
getAns();
pw.flush();
}
private int nextInt() throws Exception {
st.nextToken();
return (int) st.nval;
}
private long nextLong() throws Exception {
st.nextToken();
return (long) st.nval;
}
static final int N = 310;
static final int INF = Integer.MAX_VALUE;
int n;
int[] amount = new int[N];
int[] color = new int[N];
// 前缀和,便于计算石子合并产生的花费
int[] sum = new int[N];
// 统计该区间有多少堆石子
int[][] num = new int[N][N];
// 统计合并石子的花费
int[][] cost = new int[N][N];
int[][][] f = new int[N][N][3];
private void getAns() throws Exception {
n = nextInt();
for (int i = 1; i <= n; i++) {
for (int j = i; j <= n; j++) {
num[i][j] = j - i + 1;
if (i != j) cost[i][j] = INF;
for (int k = 0; k < 3; k++) f[i][j][k] = INF;
}
}
for (int i = 1; i <= n; i++) {
amount[i] = nextInt();
sum[i] = sum[i - 1] + amount[i];
}
for (int i = 1; i <= n; i++) {
color[i] = nextInt();
// 初始石堆花费为 0
f[i][i][color[i]] = 0;
}
for (int len = 1; len <= n; len++) { // 区间长度
for (int i = 1; i + len - 1 <= n; i++) { // 枚举起点
int j = i + len - 1; // 区间终点
for (int c = 0; c < 3; c++) {
int tmp = INF;
for (int k = i; k < j; k++) { // 枚举分割点
if(f[i][k][c]!= INF && f[k+1][j][c] != INF) // 两区间合法才进行合并
tmp = Math.min(tmp, f[i][k][c] + f[k + 1][j][c]);
}
if (tmp != INF) {
num[i][j] = 1;
f[i][j][(c + 1) % 3] = Math.min(f[i][j][(c + 1) % 3], tmp + sum[j] - sum[i - 1]);
cost[i][j] = Math.min(cost[i][j], f[i][j][(c + 1) % 3]);
}
}
for (int k = i; k < j; k++) {
if (num[i][j] > num[i][k] + num[k + 1][j]) { // 枚举分割点
num[i][j] = num[i][k] + num[k + 1][j];
cost[i][j] = cost[i][k] + cost[k + 1][j];
} else if (num[i][j] == num[i][k] + num[k + 1][j] && cost[i][j] > cost[i][k] + cost[k + 1][j]) {
cost[i][j] = cost[i][k] + cost[k + 1][j];
}
}
}
}
pw.println(num[1][n] + " " + cost[1][n]);
}
}
试题 I: 最大开支
[题解]
首先观察可发现一个项目的开销满足: x * y = k * x * x + b * x; 可看出该式子为简单二次式
要使得开销最大应该尽可能使得每个项目开销最大,即拿到上式的最大值,
其中人数应该满足 x = (-b) / (2 * k),由于人数是整数,我们要找到最接近对称轴的整数,即只要得到的 x 四舍五入即可
综上可记每个项目的开支为 y = k x 2 + b x y = kx^2 + bx y=kx2+bx, y 项目开支, x 该项目人数
若要最终开销最大,则可使得每个人选完项目开销增加的最大,可通过反证法证明该贪心是可行的
最关键的一点怎么保证每个人选完项目一定能使得开销增加最多
每次选完增加的开支: i n c r e a s e P r i c e = y i ( x ) − y i ( x − 1 ) increasePrice = y_i(x) - y_i(x - 1) increasePrice=yi(x)−yi(x−1), 只要我们保证 i n c r e a s e P r i c e increasePrice increasePrice 最大,即可得到最终答案
我们通过优先队列,保证每次得到的 i n c r e a s e P r i c e increasePrice increasePrice 最大
import java.io.*;
import java.util.PriorityQueue;
/**
* 2023 最大开支 数学 贪心 优先队列
*/
public class Main {
public static void main(String[] args) throws Exception {
new Main().go();
}
static BufferedReader br;
static StreamTokenizer st;
static PrintWriter pw;
private void go() throws Exception {
br = new BufferedReader(new InputStreamReader(System.in));
st = new StreamTokenizer(br);
pw = new PrintWriter(new OutputStreamWriter(System.out));
getAns();
pw.flush();
}
private int nextInt() throws Exception {
st.nextToken();
return (int) st.nval;
}
private long nextLong() throws Exception {
st.nextToken();
return (long) st.nval;
}
static final int N = (int)10e5 + 10;
// n 个人, m 个娱乐项目
int n, m;
// 存储票价优惠方案
int[][] prices = new int[N][2];
// 表示该娱乐项目达到最大开支的人数
int[] numMax = new int[N];
PriorityQueue<int[]> queue = new PriorityQueue<>((a, b) -> ((int)((getPrice(b[0], b[1] + 1) - getPrice(b[0], b[1])) - (getPrice(a[0], a[1] + 1) - getPrice(a[0], a[1])))));
private void getAns() throws Exception {
n = nextInt();
m = nextInt();
for (int i = 1; i <= m; i++) {
prices[i][0] = nextInt();
prices[i][1] = nextInt();
numMax[i] = getNum(prices[i][0],prices[i][1]);
queue.offer(new int[] {i, 0});
}
long ans = 0;
for (int i = 1; i <= n; i++) {
if (queue.isEmpty()) break;
int[] node = queue.poll();
if (++node[1] <= numMax[node[0]]) {
ans += getPrice(node[0], node[1]) - getPrice(node[0], node[1] - 1);
if (node[1] < numMax[node[0]]) queue.offer(node);
} else break;
}
pw.println(ans);
}
// 得到一个项目花销最大时应该的人数
private int getNum(int k, int b) {
// 由于都是 int 进行除法运算,会导致得不到浮点数,所以加个 0.0 使得其中一个变成浮点数,从而使得向上取整可行
int res = (int)(-(b / (2 * k + 0.0)) + 0.5);
return res;
}
// 计算该项目所需开销
private long getPrice(int idx, int num) {
long res = (long)(prices[idx][0] * num + prices[idx][1]) * num;
return res;
}
}
试题 J: 魔法阵
[题解]
采用 Dijkstra 算法来计算最短路径
由于可能会走回头路,采用队列来枚举接下来要走的路。
因为多了可以释放魔法的条件,再加以 DP 的方法来记录是放魔法受到伤害小,还是不放魔法受到伤害小
记录状态 f ( i , j ) f(i, j) f(i,j) 到 i 点距离释放魔法已经走了 j 个点, j ≤ k j \leq k j≤k
状态转移方程:
$ j = 0 或 j = k, f(i, j) = f(i - 1, j) + w(i)$
j = = 其他 , f ( i , j ) = f ( i − 1 , j − 1 ) j == 其他, f(i, j) = f(i - 1, j - 1) j==其他,f(i,j)=f(i−1,j−1)
最后答案 a n s = m i n { f ( n − 1 , 0 ) , f ( n − 1 , k ) } ans = min\{f(n - 1, 0), f(n - 1, k)\} ans=min{f(n−1,0),f(n−1,k)}
import java.io.*;
import java.util.*;
import java.math.*;
import java.util.stream.*;
/**
* 2023 魔法阵 Dijkstra DP
*/
public class Main {
public static void main(String[] args) throws Exception {
new Main().go();
}
static BufferedReader br;
static StreamTokenizer st;
static PrintWriter pw;
private void go() throws Exception {
br = new BufferedReader(new InputStreamReader(System.in));
st = new StreamTokenizer(br);
pw = new PrintWriter(new OutputStreamWriter(System.out));
getAns();
pw.flush();
}
private int nextInt() throws Exception {
st.nextToken();
return (int) st.nval;
}
static final int INF = Integer.MAX_VALUE;
static final int N = 1010;
int n, k, m;
int[][] grid = new int[N][N];
int[][] f = new int[N][11];
private void getAns() throws Exception {
n = nextInt();
k = nextInt();
m = nextInt();
// 初始化图
for(int i = 0; i < n; i++) {
for(int j = 0; j < n; j++) grid[i][j] = INF;
}
for(int i = 0; i < m; i++) {
int u = nextInt();
int v = nextInt();
grid[u][v] = nextInt();
grid[v][u] = grid[u][v];
}
Dijkstra();
int ans = Math.min(f[n - 1][0], f[n - 1][k]);
pw.println(ans);
}
private void Dijkstra() {
for(int i = 0; i < n; i++) {
for(int j = 0; j <= k; j++) {
f[i][j] = INF;
}
}
f[0][0] = 0;
Queue<int[]> queue = new ArrayDeque<>();
queue.offer(new int[] {0,0});
while(!queue.isEmpty()) {
int[] p = queue.poll();
// p[0] 中介点, p[1] 距离释放魔法已经过了几个点,若 p[1] == k 魔法失效
int u = p[0], j = p[1];
for(int v = 0; v < n; v++) {
if (grid[u][v] != INF) {
if(j < k && f[v][j + 1] > f[u][j]) {
f[v][j + 1] = f[u][j];
queue.offer(new int[]{v, j + 1});
}
if((j == 0 || j == k) && f[v][j] > f[u][j] + grid[u][v]) {
f[v][j] = f[u][j] + grid[u][v];
queue.offer(new int[]{v, j});
}
}
}
}
}
}
以上综合各位大佬提供给的思路和我自己的思路,自己写出的题解。