A - 321-like Checker
题目描述
给你一个数n
判断它是否为一个好数,一个好数定义为对于这个数字的第i
位是否严格小于第i-1
位即 d i < d i − 1 d_i \lt d_{i - 1} di<di−1。如果这个数是好数输出Yes
,否则输出No
。
思路:模拟+枚举
- 从第
2
位开始枚举(下标从1
开始),判断是否会出现 d i > d i − 1 d_i \gt d_{i - 1} di>di−1,若出现则不是一个好数。
public static void solve() throws IOException{
String s = readString();
boolean f = false;
int n = s.length();
for (int i = 1; i < s.length(); i++) {
if ((int)s.charAt(i) >= s.charAt(i - 1)) {
f = true;// 出现了不严格递增的情况
break;
}
}
printWriter.println(f ? "No" : "Yes");
}
B - Cutoff
题目描述
你进行了N
轮考试,即获得了N
份成绩单,你现在知道其中的N-1
份成绩单的成绩,每一份的成绩为 S i S_i Si( 0 ≤ S i ≤ 100 0 \le S_i \le 100 0≤Si≤100),还有一份成绩单丢了,你不知道是哪一份,现在需要你求出那份成绩单的分级,但是成绩要尽可能小,使得在添加上这份成绩单的成绩后,去掉成绩最高分和最低分,剩下的分数要大于等于一个整数X
,如果这份成绩单是任意分数都无法满足条件,则输出-1
,否则输出那份成绩单的分数。
思路:排序+分类讨论
- 首先假设丢的这份是最小的数,那么肯定要最小化为
0
,然后求排序好的已知的中间的前N-2
的分数总和是否满足条件,不满足则进行下一步。- 假设丢的是中间分数的某一份,那么中间 S 2 ∼ S N − 2 S_2 \sim S_{N-2} S2∼SN−2的分数加上丢失的分数使其大于等于
X
,但是这个丢失的分数要大于等于 S 1 S_1 S1,小于等于 S N − 1 S_{N-1} SN−1,不满足则进行下一步。- 最后只可能丢失分数最大的那份,那么直接判断 S 2 ∼ S N − 1 S_2 \sim S_{N-1} S2∼SN−1的分数是否大于等于
X
,不满足则输出-1
。
public static void solve() throws IOException{
int n = readInt(), k = readInt();
int[] arr = new int[n + 10];
for (int i = 1; i <= n - 1; i++) {
arr[i] = readInt();
}
Arrays.sort(arr, 1, n);
int leftSum = 0;
for (int i = 1; i <= n - 2; i++) {
leftSum += arr[i];
}
if (leftSum >= k) {//少最左边的数,用 0来补
printWriter.println(0);
} else {
//少中间的数
int midSum = 0;
for (int i = 2; i <= n - 2; i++) {
midSum += arr[i];
}
if (midSum >= k) {
printWriter.println(arr[1]);//不补数也能大于等于k,那么用arr[1]来补最好
} else {
int left = k - midSum;
if (left >= arr[1] && left <= arr[n - 1]) {
printWriter.println(left);//否则用 k - midSum来补
} else {
// 少右边的数
int rigthSum = 0;
for (int i = 2; i <= n - 1; i++) {
rigthSum += arr[i];
}
if (rigthSum >= k) {
printWriter.println(arr[n - 1]);
} else {
printWriter.println(-1);
}
}
}
}
}
C - 321-like Searcher
题目描述
一个好数定义为对于这个数字的第i
位是否严格小于第i-1
位即 d i < d i − 1 d_i \lt d_{i - 1} di<di−1。现在要你求出第K
小的好数。
思路:dfs
- 由于一个数能够凑出好数的方案数较少,爆搜即可。注意要开
long
。
static List<Long> list = new ArrayList<>();
static TreeSet<Long> set = new TreeSet<>();// 排序+去重
public static void solve() throws IOException {
int n = readInt();
for (int i = 1; i <= 9; i++) {
dfs(i, i);
}
for (long p : set){
list.add(p);
}
printWriter.println(list.get(n - 1));
}
public static void dfs(int u, long cur) {
if (cur < 0) return;
if (cur >= 0 && !set.contains(cur)) {
set.add(cur);
}
for (int i = u - 1; i >= 0; i--) {
dfs(i, 1l * cur * 10 + i);
}
}
D - Set Menu
题目描述
食堂提供N
道主菜和M
道配菜。第i
种主菜的价格是 A i A_i Ai ,第j
种配菜的价格是 B j B_j Bj 。套餐包括一道主菜和一道配菜。设主菜和配菜的价格之和为s
,则套餐的价格为 m i n ( s , P ) min(s,P) min(s,P)。选择套餐主菜和配菜的方法有 N ∗ M N*M N∗M种。求所有这些套餐的总价。
输入样例2 2 7 3 5 6 1
输出样例
24
思路:排序+二分+前缀和
- 排序不影响答案,先排序,用于二分,二分求的值看代码注释。
- 对于每一个 a i a_i ai,需要求出有多少个 b i b_i bi加上 a i a_i ai后会大于
P
,全部用P
来替代,剩下的用 a i + b i a_i+b_i ai+bi来替代, b i b_i bi用前缀和维护即可,注意开long
。
public static void solve() throws IOException {
int n = readInt(), m = readInt(), k = readInt();
int[] a = utils.nextIntArray(n), b = utils.nextIntArray(m);
Arrays.sort(b, 1, m + 1);
long[] pre = new long[m + 1];
for (int i = 1; i <= m; i++) {
pre[i] = 1l * b[i] + pre[i - 1];
}
long sum = 0;
for (int i = 1; i <= n; i++) {
int left = k - a[i];
int l = 0, r = m + 1;
// 找到最后一个小于等于 left的坐标
while (l + 1 < r) {
int mid = l + r >> 1;
if (b[mid] <= left) {
l = mid;
} else {
r = mid;
}
}
sum += 1l * (m - l) * k;// (m-l)个数加上a[i]后会大于k,所以有(m-1)个b[i]被 k替代
// [1,l]中的数加上a[i]全都小于等于k
sum += 1l * l * a[i];// 有l个a[i]
sum += pre[l];// 前[1,l]个b[i]之和
}
printWriter.println(sum);
}