一、4376. 数圈圈(简单)
暴力就行,唯一要注意的是Java的Integer.toString(),会把十六进制的A、B、C…转成小写!!!(被罚时了)
import java.util.*;
import java.io.*;
public class Main {
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
Scanner scan = new Scanner(System.in);
int n = scan.nextInt();
String tmp = Integer.toString(n, 16);
char[] str = tmp.toCharArray();
int cnt = 0;
for (char cur : str) {
if (cur == '0' || cur == '4' || cur == '6' || cur == '9' || cur == 'a' || cur == 'd') {
cnt++;
} else if (cur == '8' || cur == 'b') {
cnt += 2;
}
}
System.out.println(cnt);
}
}
二、4377. 农田灌溉(中等)
有了这周周赛的锻炼,对于这种区间问题终于是有思路了。
如上图分析所示,外层循环遍历农田,内层循环遍历所有可能的洒水器,只需要找距离当前农田最近的洒水器即可。
import java.util.*;
import java.io.*;
public class Main {
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
public static void main(String[] args) throws IOException {
String[] input = reader.readLine().trim().split(" ");
int t = Integer.parseInt(input[0]);
while (t-- > 0) {
input = reader.readLine().trim().split(" ");
int n = Integer.parseInt(input[0]); // n片农田
int k = Integer.parseInt(input[1]); // k个装有洒水器的农田
boolean[] have = new boolean[n + 1];
input = reader.readLine().trim().split(" ");
for (int i = 0; i < k; i++) {
// 标记有洒水器的地方
have[Integer.parseInt(input[i])] = true;
}
int ans = 1;
for (int i = 1; i <= n; i++) {
if (have[i]) {
continue;
}
int min = Integer.MAX_VALUE;
// 没有喷水器,去找最近的喷水器
for (int j = 1; j <= n; j++) {
if (have[j] && Math.abs(j - i) < min) {
min = Math.abs(j - i);
}
}
ans = Math.max(ans, min + 1);
}
log.write(ans + "\n");
}
log.flush();
}
}
三、4378. 选取数对(困难)
一开始的思路是搜索,超时了,搜索可以转成DP,所以最后用DP完成了最后一击,成功AK。
题目意思:需要找到 k 个 长度为 m 的数对,每个数对不能包含重复的元素,要求所有数对的和的最大值。长度为 m 的数对,也就是长度为 m 的区间和,也就是需要用preSum去找区间和,因为数对长度必须为 m,所以可以从第 m 个数开始枚举,对于当前数对,可以选择、不选,选的话,下标就得跳到 i + m,如果不选的话下标跳到 i + 1,最后找完所有的 k 个数对,求最大值即可,用DFS很容易做:
import java.util.*;
import java.io.*;
public class Main {
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
static long[] preSum;
static int n, m, k;
static long max = 0;
public static void main(String[] args) throws IOException {
String[] input = reader.readLine().trim().split(" ");
n = Integer.parseInt(input[0]);
m = Integer.parseInt(input[1]);
k = Integer.parseInt(input[2]);
preSum = new long[n + 1];
input = reader.readLine().trim().split(" ");
for (int i = 1; i <= n; i++) {
preSum[i] = preSum[i - 1] + Long.parseLong(input[i - 1]);
}
// k个数对,数对长度为m(前缀和长度),和最大
dfs(m, 0, 0); // 从第m个下标开始搜(才能确保长度为m),数对个数 = 0
log.write(max + "");
log.flush();
}
static void dfs(int index, int cnt, long cur) {
if (cnt == k) {
// 找到了k对数
max = Math.max(max, cur);
return;
}
if (index > n) return;
// 保证数对长度
long tmp = preSum[index] - preSum[index - m];
dfs(index + m, cnt + 1, cur + tmp);
// 也可以不要这个数
dfs(index + 1, cnt, cur);
}
}
只过了13个数据,显然是因为数据量太大,超时了。
仔细想想,当前数对选、不选,都会对后面的答案产生影响,而我们要最大值,后面的答案可以由前面已有的结果推出,所以可以用DP数组进行求解。
直接写出DP还是有难度,但是从DFS转到DP就简单了很多,因为你知道到底在搜索什么东西,也就是DP需要记录的状态。
import java.util.*;
import java.io.*;
public class Main {
static BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
static BufferedWriter log = new BufferedWriter(new OutputStreamWriter(System.out));
static long[] preSum;
static int n, m, k;
static long max = 0;
public static void main(String[] args) throws IOException {
String[] input = reader.readLine().trim().split(" ");
n = Integer.parseInt(input[0]);
m = Integer.parseInt(input[1]);
k = Integer.parseInt(input[2]);
preSum = new long[n + 1];
input = reader.readLine().trim().split(" ");
for (int i = 1; i <= n; i++) {
preSum[i] = preSum[i - 1] + Long.parseLong(input[i - 1]);
}
// dp[i][j]: 前 i 个数,在 j 个数对的情况下的最大数对和
long[][] dp = new long[n + 1][k + 1];
for (int i = m; i <= n; i++) { // 从m开始,避免第一个数对不满足题意
// 对于当前一个数对,可以考虑其初始值:preSum[i] - preSum[i - m]
dp[i][1] = preSum[i] - preSum[i - m];
for (int j = 1; j <= k; j++) {
long tmp = preSum[i] - preSum[i - m];
// 当前数对可以不选:dp[i - 1][j],选:dp[i - m][j - 1] + tmp,选的话不能和前面的选择重复,所以必须得跳到i - m
dp[i][j] = Math.max(dp[i - 1][j], dp[i - m][j - 1] + tmp);
}
}
log.write(dp[n][k] + "");
log.flush();
}
}
BTW,没想到这一次能够AK,第三题看着DFS超时,心态就有点崩,但静下来认真思考,终于在最后转成了DP,泪目!!纪念!