题目描述
有
8
个整数 S 1 , S 2 , . . . S 8 S_1,S_2,...S_8 S1,S2,...S8,如果这8
个数满足下面3
个条件,输入Yes
,否则输出No
。
- S 1 ∼ S 8 S_1 \sim S_8 S1∼S8是单调非递减的,即 S 1 ≤ S 2 ≤ . . . ≤ S 8 S_1 \le S_2 \le ... \le S_8 S1≤S2≤...≤S8
- S 1 ∼ S 8 S_1 \sim S_8 S1∼S8都处于
[100,675]
- S 1 ∼ S 8 S_1 \sim S_8 S1∼S8都是
25
的倍数思路
- 模拟
public static void solve() throws IOException {
int cur = 0;
for (int i = 0; i < 8; i++) {
int a = readInt();
if (a < 100 || a > 675 || a % 25 != 0) {
printWriter.println("No");
return;
}
if (i == 0) {
cur = a;
} else {
if (a < cur) {
printWriter.println("No");
return;
} else {
cur = a;
}
}
}
printWriter.println("Yes");
}
题目描述
高桥在餐厅吃了
N
个寿司,每个寿司的颜色用一个字符串 C i C_i Ci表示。餐厅的总共有M
个寿司,编号为 1 ∼ M 1 \sim M 1∼M每个寿司 D i D_i Di的颜色有着对应的价格 P i P_i Pi,如果高桥吃的寿司 C i C_i Ci的颜色在餐厅中不存在,则这个寿司的价格是 P 0 P_0 P0。求高桥最后吃的寿司总价格。思路
- 模拟
public static void solve() throws IOException {
int n = sc.nextInt(), m = sc.nextInt();
String[] strings = new String[n + 1];
for (int i = 1; i <= n; i++) {
strings[i] = sc.next();//吃的寿司
}
String[] strings2 = new String[m + 1];
for (int i = 1; i <= m; i++) {
strings2[i] = sc.next();//餐厅的寿司
}
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i <= m; i++) {
int a = sc.nextInt();
if (i == 0) {
map.put("TEMP", a);
} else {
map.put(strings2[i], a);
}
}
int res = 0;
for (int i = 1; i <= n; i++) {
String s = strings[i];
res += map.containsKey(s) ? map.get(s) : map.get("TEMP");//不存在则价格为P0
}
printWriter.println(res);
}
题目描述
有
N
个人,编号为 1 ∼ N 1 \sim N 1∼N, 他们分别抛硬币,抛到正面的概率为 A i A_i Ai,抛到反面的概率为 B i B_i Bi,定义一个人抛硬币的成功率为 A i A i + B i \frac{A_i} {A_i + B_i} Ai+BiAi。将 1 , … , N 1,…,N 1,…,N人按其成功率从高到低排序,成功率相同则按编号从小到大排序。思路
- 模拟(注意精度问题)
static class Pair{
Long a, b;
int num;
public Pair (Long a, Long b, int num) {
this.a = a;
this.b = b;
this.num = num;
}
}
public static void solve() throws IOException {
int n = readInt();
List<Pair> list = new ArrayList<>();
for (int i = 1; i <= n; i++) {
int a = readInt(), b = readInt();
list.add(new Pair((long) a, (long) (a + b), i));
}
Collections.sort(list, new Comparator<Pair>() {
@Override
public int compare(Pair o1, Pair o2) {
long a = o1.a, b = o1.b, c = o2.a, d = o2.b;
int num1 = o1.num, num2 = o2.num;
if (a * d == b * c) {
return num1 - num2;
}
// 为什么是 b*c在前?假设a=1,b=2,c=3,d=4,那么b*c > a*d,即可实现 a/(a+b)升序排序
return b * c - a * d > 0 ? 1 : -1;
}
});
for (Pair pair : list) {
printWriter.print(pair.num + " ");
}
}
解决精度问题:有时会出现一个整数除以一个整数得到浮点数
d
d
d,但是这个
d
d
d又需要被存储从而用来排序,但是由于精度丢失问题,导致实际的排序与实际结果不同。例如比较
a
b
\frac {a} {b}
ba和
c
d
\frac {c} {d}
dc的大小,如果通过double
来比较,可能会出现精度问题导致结果错误,解决方法是将左右两边都乘以
b
d
bd
bd相当于比较
a
∗
d
a*d
a∗d和
b
∗
c
b*c
b∗c的大小,但是要注意数据范围,可能会溢出,开long
就好了。
题目描述
有一个
H
行W
列的网格,史努克位于左上角,问是否存在一条这样的路径: s → n → u → k → e → s → n → . . . s \to n \to u \to k \to e \to s \to n \to ... s→n→u→k→e→s→n→...,史努克可以朝着上下左右四个方向移动,从而让史努克到达右下角,如果存在,输出Yes
,否则输出No
。思路
- bfs
static int n, m;
static char[][] map;
static boolean[][] st;
static int[] dx = new int[] {-1, 0, 1, 0}, dy = new int[] {0, 1, 0, -1};
public static void solve() throws IOException {
n = sc.nextInt(); m = sc.nextInt();
map = new char[n + 1][m + 1];
st = new boolean[n + 1][m + 1];
for (int i = 1; i <= n; i++) {
map[i] = (" " + sc.next()).toCharArray();//空格占位,从第一列开始枚举
}
printWriter.println(bfs() ? "Yes" : "No");
}
public static boolean bfs() {
if (map[1][1] != 's') return false;
Deque<int[]> deque = new LinkedList<>();
deque.offer(new int[] {1, 1});
st[1][1] = true;
while (deque.size() > 0) {
int[] t = deque.poll();
if (t[0] == n && t[1] == m) return true;//可以到达
char ch = map[t[0]][t[1]];
boolean f = false;
for (int i = 0; i < 4; i++) {
int nx = t[0] + dx[i], ny = t[1] + dy[i];
if (nx >= 1 && nx <= n && ny >= 1 && ny <= m && !st[nx][ny]) {
//枚举五种情况
if (ch == 's' && map[nx][ny] == 'n') {
deque.offer(new int[] {nx, ny});
st[nx][ny] = true;
} else if (ch == 'n' && map[nx][ny] == 'u') {
deque.offer(new int[] {nx, ny});
st[nx][ny] = true;
} else if (ch == 'u' && map[nx][ny] == 'k') {
deque.offer(new int[] {nx, ny});
st[nx][ny] = true;
} else if (ch == 'k' && map[nx][ny] == 'e') {
deque.offer(new int[] {nx, ny});
st[nx][ny] = true;
} else if (ch == 'e' && map[nx][ny] == 's') {
deque.offer(new int[] {nx, ny});
st[nx][ny] = true;
}
}
}
}
return false;
}
题目描述
有个长度为
N
的序列 A = ( A 1 , A 2 , … , A N ) A=(A_1,A_2,\dots,A_N) A=(A1,A2,…,AN),由 0 0 0、 1 1 1 和 2 2 2 组成,以及一个长度为N
的字符数组 S = S 1 S 2 … S N S=S_1S_2…S_N S=S1S2…SN ,由M
、E
和X
组成。对于所有符合条件 1 ≤ i < j < k ≤ N 1 \le i \lt j \lt k \le N 1≤i<j<k≤N且 S i S j S k S_iS_jS_k SiSjSk=MEX
的三元组 ( i , j , k ) (i,j,k) (i,j,k),请你求出 m e x ( A i , A j , A k ) mex(A_i,A_j,A_k) mex(Ai,Aj,Ak)之和,其中 m e x mex mex函数表示未出现在序列中的最小非负整数。样例输入
4 1 1 0 2 MEEX
样例输出
3
样例解释:
使得 S i S j S k S_iS_jS_k SiSjSk=
MEX
的元组有以下两个: ( i , j , k ) = ( 1 , 2 , 4 ) , ( 1 , 3 , 4 ) (i,j,k)=(1,2,4),(1,3,4) (i,j,k)=(1,2,4),(1,3,4).因为 mex ( A 1 , A 2 , A 4 ) = mex ( 1 , 1 , 2 ) = 0 \text{mex}(A_1,A_2,A_4)=\text{mex}(1,1,2)=0 mex(A1,A2,A4)=mex(1,1,2)=0(因为最小未出现的数字是0)和 mex ( A 1 , A 3 , A 4 ) = mex ( 1 , 0 , 2 ) = 3 \text{mex}(A_1,A_3,A_4)=\text{mex}(1,0,2)=3 mex(A1,A3,A4)=mex(1,0,2)=3(因为最小未出现的数字是3),所以答案是 0 + 3 = 3 0+3=3 0+3=3。思路
- 先求出
mex
函数,即求出3
个数中为出现的最小的数- 遍历字符数组
S
中的所有E
,分别求出当前E
的前面的M
和后面的X
的个数
public static void solve() throws IOException {
int n = sc.nextInt();
int[] nums = new int[n + 1];
char[] chars = new char[n + 1];
for (int i = 1; i <= n; i++) {
nums[i] = sc.nextInt();
}
chars = (" " + sc.next()).toCharArray();//空格占位
//ms统计当前位置的E的前面有多少个M
//xs统计当前位置的E的后面有多少个X
int[][] ms = new int[n + 1][3], xs = new int[n + 1][3];
int m0 = 0, m1 = 0, m2 = 0, x0 = 0, x1 = 0, x2 = 0;
//统计当前 e的前面有多少个 m和它后面有多少个 x
for (int i = 1, j = n; i <= n; i++, j--) {
if (chars[i] == 'E') {
ms[i][0] = m0; ms[i][1] = m1; ms[i][2] = m2;
} else if (chars[i] == 'M') {
if (nums[i] == 0) m0++;
else if (nums[i] == 1) m1++;
else if (nums[i] == 2) m2++;
}
if (chars[j] == 'E') {
xs[j][0] = x0;
xs[j][1] = x1;
xs[j][2] = x2;
} else if (chars[j] == 'X') {
if (nums[j] == 0) x0++;
else if (nums[j] == 1) x1++;
else if (nums[j] == 2) x2++;
}
}
long sum = 0;
for (int i = 2; i <= n - 1; i++) {//从 2枚举到 n-1
if (chars[i] == 'E') {
for (int j = 0; j < 3; j++) {
if (ms[i][j] == 0) continue;//当前E前面没有mj
for (int k = 0; k < 3; k++) {
if (xs[i][k] == 0) continue;//当前E后面没有xk
sum += (long) mex(nums[i], j, k) * ms[i][j] * xs[i][k];//统计,乘法原理
}
}
}
}
printWriter.println(sum);
}
//求出三个数中最小的且没有出现过的数
public static int mex(int x, int y, int z) {
for (int i = 0; i <= 2; i++) {
if (x != i && y != i && z != i) return i;
}
return 3;
}
考点:前后缀分解
题目描述
你想在商店里买
N
件商品,第i
件商品的价格为 P i P_i Pi,现在你有M
张优惠券,第i
张优惠券需要满 L i L_i Li减 D i D_i Di,一件物品最多只能使用一张优惠券,每一张优惠券最多使用一次。现在请你求出购买所有N
件商品所需的最小总金额。思路
- 先按照优惠券的优惠力度 D i D_i Di降序排序,这样可以尽可能地减少花费
- 再按照优惠券需要满足的 L i L_i Li降序排序,这样可以尽可能地多用优惠券,间接减少花费
- 用
TreeMap
的ceilingKey()
方法获取最小的可以满足 L i L_i Li价格的商品
public static void solve() throws IOException {
int n = readInt(), m = readInt();
long sum = 0;
TreeMap<Integer, Integer> map = new TreeMap<>();
for (int i = 1; i <= n; i++) {
int a = readInt();
map.put(a, map.getOrDefault(a, 0) + 1);
sum += a;//先统计所有商品的价格之和,即将所有商品购买
}
//优先队列先按照Di降序排序,再按照Li降序排序
PriorityQueue<int[]> deque = new PriorityQueue<>(new Comparator<int[]>() {
@Override
public int compare(int[] o1, int[] o2) {
if (o1[1] == o2[1]) {
return o2[0] - o1[0];
}
return o2[1] - o1[1];
}
});
int[] l = new int[m + 1], d = new int[m + 1];
for (int i = 1; i <= m; i++) {
l[i] = readInt();
}
for (int i = 1; i <= m; i++) {
d[i] = readInt();
}
for (int i = 1; i <= m; i++) {
deque.offer(new int[] {l[i], d[i]});
}
//不断枚举优惠券,做到能用则用
while (deque.size() > 0) {
int[] t = deque.poll();
Integer key = map.ceilingKey(t[0]);//获取大于等于t[0]的最小的键,O(logn)
if (key != null) {
sum -= t[1];//可以使用,则花费总额减少Di
map.put(key, map.getOrDefault(key, 0) - 1);
if (map.get(key) == 0) map.remove(key);//优惠券用完就移除
}
}
printWriter.println(sum);
}
考点:1. 优先队列的使用和排序方式 2. TreeMap
的ceilingKey()
方法的使用