从左到右每列贪心取即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static void solve () { int m = io.nextInt(), n = io.nextInt(); String[] g = new String [m]; for (int i = 0 ; i < m; i++) { g[i] = io.next(); } int idx = 0 ; String s = "vika" ; for (int i = 0 ; i < n; i++) { for (int j = 0 ; j < m; j++) { if (g[j].charAt(i) == s.charAt(idx)) { idx++; break ; } } if (idx == s.length()) { io.println("YES" ); return ; } } io.println("NO" ); }
构造题。当 \(b_{i-1}>b_{i}\) 时,在两个数中间添加一个 \(1\) 即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public static void solve () { int n = io.nextInt(); int [] b = new int [n]; for (int i = 0 ; i < n; i++) { b[i] = io.nextInt(); } List<Integer> ans = new ArrayList <>(); ans.add(b[0 ]); for (int i = 1 ; i < n; i++) { if (b[i] < b[i - 1 ]) ans.add(1 ); ans.add(b[i]); } io.println(ans.size()); for (int x : ans) io.print(x + " " ); io.println(); }
阅读理解。题目中的“对角线对称”这个概念根本不用管,就是不断对区间做加法,然后判断是否和原数组相等,可以使用差分 + 前缀和解决。看完题解,发现其实也可以 \(O(1)\) 空间解决,因为数组是非递增的,按顺序遍历就行,具体见代码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static void solve () { int n = io.nextInt(); int [] a = new int [n]; for (int i = 0 ; i < n; i++) { a[i] = io.nextInt(); } for (int i = 0 , j = n; i < n; i++) { for (; j > 0 && a[j - 1 ] <= i; j--) ; if (a[i] != j) { io.println("NO" ); return ; } } io.println("YES" ); }
题目描述很有问题,其实就是问从一个长度为 \(m\) 的序列中选两个值组成集合,使得不同集合的数目恰好 为 \(n\) 的 \(m\) 是多少。可以先二分求 \(x\),使得 \(C_{x}^{2}\leq n\) 且 \(C_{x+1}^{2}>n\)。然后答案就是 \(x+(n-C_{x}^{2})\),表示 \([1,n-C_{x}^{2}]\) 范围内的每个数各取两个,以及 \([n-C_{x}^{2}+1,x]\) 范围内的每个数各取一个。PS:读题很容易漏掉恰好两个字。
1 2 3 4 5 6 7 8 9 10 public static void solve () { long n = io.nextLong(); long lo = 2 , hi = (long ) 1e9 * 2 ; while (lo <= hi) { long mid = lo + (hi - lo) / 2 ; if (mid * (mid - 1 ) / 2 <= n) lo = mid + 1 ; else hi = mid - 1 ; } io.println(hi + (n - hi * (hi - 1 ) / 2 )); }
做这道题时漏掉“开业前一天去过电影院”这个条件,导致想了半天。答案要求最多看 \(m\) 部电影的最大娱乐价值,首先我们可以观察到娱乐值的下降幅度只与最后一次去电影院的日期 \(x\) 有关,即下降幅度为 \(x\cdot d\)。所以我们可以从前往后枚举 \(x\),并且维护最大长度为 \(m\) 的优先队列,来保证最多看 \(m\) 部电影。需要注意电影的娱乐值可能是负数,而我们只需要在优先队列中存储正数即可。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static void solve () { int n = io.nextInt(), m = io.nextInt(), d = io.nextInt(); int [] a = new int [n]; for (int i = 0 ; i < n; i++) a[i] = io.nextInt(); PriorityQueue<Integer> q = new PriorityQueue <>(); long sum = 0L , ans = 0L ; for (int i = 0 ; i < n; i++) { if (a[i] <= 0 ) continue ; q.offer(a[i]); sum += a[i]; if (q.size() > m) sum -= q.poll(); ans = Math.max(ans, sum - (long ) d * (i + 1 )); } io.println(ans); }
初见时想到的是二分时间 + 动态规划,赛后优化发现可以直接动态规划做。我是用背包做的,\(dp[i][j]\) 表示前 \(i\) 个怪物使用 \(j\) 点法术值能够击败的怪物总强度最大是多少,然后枚举水法术值计算答案。但是其实可以不用这样,我们只需要知道怪物的子集的所有可能强度是多少,然后枚举所有能够到达的强度即可。(C++ 位图很方便)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 public static void solve () { int w = io.nextInt(), f = io.nextInt(), n = io.nextInt(); int sum = 0 ; int [] s = new int [n]; for (int i = 0 ; i < n; i++) { s[i] = io.nextInt(); sum += s[i]; } boolean [] dp = new boolean [sum + 1 ]; dp[0 ] = true ; for (int i = 0 ; i < n; i++) { for (int j = sum; j >= s[i]; j--) { dp[j] = dp[j] || dp[j - s[i]]; } } int ans = Integer.MAX_VALUE; for (int i = 0 ; i <= sum; i++) { if (!dp[i]) continue ; ans = Math.min(ans, Math.max((i + w - 1 ) / w, (sum - i + f - 1 ) / f)); } io.println(ans); }
很容易就可以得出结论,设备的输出值是数组的最大值 + 排序后相邻元素的最大差值,但是不知道怎么维护。使用 C++ 的 multiset 很容易写,详细见大佬的代码 。