拼多多笔试0420
贪心+多路选择
- 对每个字符串 Si,提取其字符并按字典序排序,只保留前 Xi 个最小的字符。
- 将所有保留的字符放到一个总候选池中。
- 对这个大池子进行字典序排序,选前 K 个字符,拼成字符串 T。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| import java.util.*;
public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int N = in.nextInt(); int K = in.nextInt(); StringBuffer sb = new StringBuffer(); for (int i = 0; i < N; i++) { String s = in.next(); int x = in.nextInt(); char[] chars = s.toCharArray(); Arrays.sort(chars); sb.append(new String(chars, 0, x)); } char[] candidates = sb.toString().toCharArray(); Arrays.sort(candidates); String ans = new String(candidates, 0, K); System.out.println(ans); } }
|
- 时间复杂度:假设读取的字符串平均长度之和为L,$O(L\cdotlogL)$。
- 空间复杂度:O(L)
拓扑排序
图建模:
- 将每个演员视为图中的一个节点。
- 每条意见(ai, bi)表示一条有向边,从bi指向ai(表示ai片酬 > bi片酬)。
拓扑排序检测是否有环:
- 如果图中存在环,则说明存在矛盾意见(比如A > B, B > C, C > A),无法满足所有约束,输出-1。
最小化总片酬:
- 每位演员最少拿100元,调整是10元单位。
- 将该问题看作在满足拓扑顺序的前提下,用最少的“级别”分配每个节点的权值。
- 从入度为0的节点开始,进行拓扑排序,并将每个演员的片酬设置为满足约束所需的最小值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
| import java.util.*;
public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int T = in.nextInt(); for (int t = 0; t < T; t++) { int n = in.nextInt(); int m = in.nextInt(); List<List<Integer>> graph = new ArrayList<>(); int[] inDegree = new int[n + 1]; int[] salaryLevel = new int[n + 1]; for (int i = 0; i <= n; i++) { graph.add(new ArrayList<>()); } for (int i = 0; i < m; i++) { int a = in.nextInt(); int b = in.nextInt(); graph.get(b).add(a); inDegree[a]++; } Queue<Integer> queue = new LinkedList<>(); for (int i = 1; i <= n; i++) { if (inDegree[i] == 0) { queue.offer(i); } } int cnt = 0; while (!queue.isEmpty()) { int cur = queue.poll(); cnt++; for (int nxt : graph.get(cur)) { if (salaryLevel[nxt] < salaryLevel[cur] + 1) { salaryLevel[nxt] = salaryLevel[cur] + 1; } if (--inDegree[nxt] == 0) { queue.offer(nxt); } } } if (cnt != n) { System.out.println(-1); } else { long total = 0L; for (int i = 1; i <= n; i++) { total += 100 + 10 * salaryLevel[i]; } System.out.println(total); } } } }
|
- 时间复杂度:O(m+n)
- 空间复杂度:O(m+n)
贪心 + 二分查找
在原序列中,找到最长的严格递增子序列(LIS),最少调整的点数 = n - LIS长度。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| import java.util.Scanner;
public class Main { public static void main(String[] args) { Scanner in = new Scanner(System.in); int T = in.nextInt(); for (int t = 0; t < T; t++) { int n = in.nextInt(); int[] heights = new int[n]; for (int i = 0; i < n; i++) { heights[i] = in.nextInt(); } System.out.println(n - lengthOfLIS(heights)); } } private static int lengthOfLIS(int[] nums) { int ng = 0; for (int x : nums) { int j = lowerBound(nums, 0, ng - 1, x); nums[j] = x; if (j == ng) { ng++; } } return ng; }
private static int lowerBound(int[] nums, int left, int right, int target) { while (left <= right) { int mid = left + (right - left) / 2; if (nums[mid] < target) { left = mid + 1; } else { right = mid - 1; } } return left; } }
|
- 时间复杂度:O(nlogn)
- 空间复杂度:O(1)