2.1 初级
(1)尺取法
⭐ 反向扫描(左右指针)
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
for( ; n > 0; n--) {
char[] s = sc.next().toCharArray();
boolean ans = true;
int i = 0, j = s.length - 1;
while(i < j) {
if (s[i] != s[j]) {
ans = false; break;
}
i++; j--;
}
System.out.println(ans ? "yes" : "no");
}
}
}
⭐ 同向扫描(快慢指针)
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int n = sc.nextInt();
int S = sc.nextInt();
int[] a = new int[n];
for(int i = 0; i < n; i++)
a[i] = sc.nextInt();
int left = 0, right = 0, val = 0, ans = n + 1;
while (right < n) {
while (val >= S) {
ans = Math.min(ans, right - left);
val -= a[left++];
}
val += a[right++];
}
System.out.println(ans == n + 1 ? 0 : ans);
sc.close();
}
}
(2)二分法
⭐ 整数二分
1 在单调递增序列中找左边 \(x\) 或 \(x\) 的后继(第一个大于 \(x\) 的数)
def binSearchBack(arr: List, x: int) -> int:
# 当 x 大于所有值返回 N 故 right 赋值为 N
left, right = 0, len(arr)
while left < right:
mid = (left + right) >> 1 # 左中位数
if arr[mid] >= x:
right = mid
else:
left = mid + 1
return left
2 在单调递增序列中找右边 \(x\) 或 \(x\) 的前驱(最后一个小于 \(x\) 的数)
def binSearchFront(arr: List, x: int) -> int:
# 当 x 小于所有值返回 -1 故 left 赋值为 -1
left, right = -1, len(arr) - 1
while left < right:
mid = (left + right + 1) >> 1 # 右中位数
if arr[mid] <= x:
left = mid
else:
right = mid - 1
return left
import java.util.Scanner;
import java.util.Arrays;
public class Main {
static int N, C;
static int[] x;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
N = sc.nextInt();
C = sc.nextInt();
x = new int[N];
for(int i = 0; i < N; i++)
x[i] = sc.nextInt();
Arrays.sort(x);
int left = 1, right = x[N - 1] - x[0];
// 单调递减找左边第一个满足的值
while(left < right) {
// (3)由于左边不多移动 所以使用右中位数
int mid = (left + right + 1) >> 1;
if(check(mid))
left = mid; // (1)满足条件 左边不多移动一格
else
right = mid - 1; // (2)不满足条件 右边需要多移动一格
}
System.out.println(left);
sc.close();
}
static boolean check(int dis) {
int cnt = 1, place = 0; // 第一个牛放第一个
for(int i = 1; i < N; i++) {
if(x[i] - x[place] >= dis) {
cnt += 1; place = i;
}
if(cnt >= C) return true;
}
return false;
}
}
⭐ 实数二分
import java.util.Scanner;
public class Main {
static int T, N, F;
static double eps = 1e-5;
static double[] areas;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
T = sc.nextInt();
for( ; T > 0; T--) {
N = sc.nextInt(); F = sc.nextInt();
areas = new double[N];
double left = 0, right = 0;
for(int i = 0; i < N; i++) {
double r = sc.nextDouble();
areas[i] = Math.PI * r * r;
right = Math.max(right, areas[i]);
}
while(right - left > eps) {
double mid = left + (right - left) / 2;
if(check(mid))
left = mid;
else
right = mid;
}
System.out.println(String.format("%.4f", left));
}
}
static boolean check(double area) {
int cnt = 0;
for(int i = 0; i < N; i++) {
cnt += (int)(areas[i] / area);
if(cnt >= F + 1) return true;
}
return false;
}
}
(3)三分法
⭐ 实数三分
import java.util.Scanner;
public class Main {
static int N;
static double eps = 1e-6;
static double[] a;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
N = sc.nextInt(); a = new double[N + 1];
double l = sc.nextDouble();
double r = sc.nextDouble();
for(int i = N; i >= 0; i--)
a[i] = sc.nextDouble();
while(r - l > eps) {
double k = (r - l) / 3.0;
double mid1 = l + k, mid2 = r - k;
if(f(mid1) > f(mid2))
r = mid2;
else
l = mid1;
}
System.out.println(String.format("%.5f", l));
}
static double f(double x) {
double ans = 0; // 多项式乘法
for(int i = N; i >= 0; i--)
ans = ans * x + a[i];
return ans;
}
}
⭐ 整数三分
import java.util.Scanner;
public class Test {
static long A, B, C, ans = Long.MAX_VALUE;
static int n, m, tMin = Integer.MAX_VALUE;
static int[] t, b;
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
A = sc.nextLong(); B = sc.nextLong(); C = sc.nextLong();
n = sc.nextInt(); m = sc.nextInt();
t = new int[n]; b = new int[m];
for(int i = 0; i < n; i++) {
t[i] = sc.nextInt(); tMin = Math.min(tMin, t[i]);
}
for(int i = 0; i < m; i++) b[i] = sc.nextInt();
if(C >= 1e16) { // 有特例需要特判 否则导致数字过大
// 由于 C 过大 故所有科目均须在最小期望天数之前公布
ans = calc1(tMin);
} else {
// 不愉快度是时间的下凹函数 采取整数三分策略
int L = 1, R = 100000;
while(R - L > 2) {
int mid1 = L + (R - L) / 3, mid2 = R - (R - L) / 3;
long c1 = calc1(mid1) + calc2(mid1); // c1 总不满意度
long c2 = calc1(mid2) + calc2(mid2); // c2 总不满意度
if(c1 > c2) L = mid1;
else R = mid2;
}
for(int i = L; i <= R; i++) // 寻找此区间的最小值
ans = Math.min(ans, calc1(i) + calc2(i));
}
System.out.println(ans);
}
static long calc1(int time) { // 所有科目在 time 之前公布的不满意度
// 不满意度来自操作1 和 操作2
long x = 0, y = 0; // x 记录需增加天数 y 记录需减少天数
for(int i = 0; i < m; i++) {
if(b[i] > time) y += b[i] - time;
else x += time - b[i];
}
if(A < B) { // 由于操作1 会导致 +1 和 -1 故最大操作次数为 min(x, y)
// 增加的天数可以不增加 减少的天数必须减少 故剩余天数使用操作二执行
return Math.min(x, y) * A + (y - Math.min(x, y)) * B;
} else { // 如果操作2 的花费小于操作1 的花费 只需使用操作2
return y * B; // 只需将超过 time 的科目变为 time 即可
}
}
static long calc2(int time) { // 所有科目在 time 之前公布学生的不满意度
long sum = 0;
for(int i = 0; i < n; i++)
sum += time > t[i] ? (time - t[i]) * C : 0;
return sum;
}
}
(4)排序与排列
⭐ 排序
import java.util.Arrays;
import java.util.Comparator;
import java.util.Scanner;
public class Test {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
int N = sc.nextInt();
int[][] stus = new int[N][5];
for(int i = 0; i < N; i++) {
stus[i][0] = i + 1; stus[i][1] = sc.nextInt();
stus[i][2] = sc.nextInt(); stus[i][3] = sc.nextInt();
stus[i][4] = stus[i][1] + stus[i][2] + stus[i][3];
}
Arrays.sort(stus, Comparator.comparingInt(
(int[] stu) -> -stu[4]).thenComparingInt(
stu -> -stu[1]).thenComparingInt(
stu -> stu[0]));
for(int i = 0; i < 5; i++) {
System.out.println(stus[i][0] + " " + stus[i][4]);
}
}
}
⭐ 排列
public class Main {
static boolean[] vis = new boolean[14];
static int[] b = new int[14];
static int ans;
public static void main(String[] args) {
dfs(1); // 从第一个空开始填写数字
System.out.println(ans);
}
static void dfs(int cur) {
if(cur == 13) {
if(b[10] * b[11] == b[12])
ans++;
return;
}
if(cur == 4 && b[1] + b[2] != b[3]) return;
if(cur == 7 && b[4] - b[5] != b[6]) return;
if(cur == 10 && b[7] * b[8] != b[9]) return;
for(int i = 1; i <= 13; i++) {
if(!vis[i]) {
vis[i] = true;
b[cur] = i; dfs(cur + 1);
vis[i] = false;
}
}
}
}