os: 打完跑去睡觉,醒来后研究E题发现有更妙的写法,太妙了
A. Setting up Camp
题目:
给三种人的数量,内向人只能单独一个帐篷,外向人必须凑满三个人一个帐篷,普遍人随意安排,求最小的帐篷数量来满足以上所有人的需求
思路:
需要满足每个人的需求,外向人如果凑不齐三个就只能从普遍人中去凑,如果凑不齐就是没有可满足的方案;
package cf.contest.R935;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
public class A {
private static InputStream is = System.in;
private static final BufferedReader in = new BufferedReader(new InputStreamReader(is));
private static final PrintWriter out = new PrintWriter(System.out);
static void solve() throws IOException {
String[] ins = in.readLine().split(" ");
int a = Integer.parseInt(ins[0]), b = Integer.parseInt(ins[1]), c = Integer.parseInt(ins[2]);
int ans = 0;
ans += a; // a单独占一个帐篷
ans += b / 3; // b 凑满三个人
int bl = b % 3; // b 剩余的人需要和c凑满三个人
if (bl != 0 && bl + c < 3) { // 如果凑不满
out.println(-1);
return;
} else {
if (bl > 0) { // b和c凑满三个人
ans ++;
c -= 3 - bl;
}
// 给c分配帐篷,凑满三个人 c / 3的上取整
ans += (c + 3 - 1) / 3;
}
out.println(ans);
}
public static void main(String[] args) throws IOException {
int t = 1;
t = Integer.parseInt(in.readLine());
while (t -- > 0) {
solve();
}
in.close();
out.flush();
out.close();
}
}
B. Fireworks
题目:
有两个烟花发射器,第一个隔a分钟发射一次,第二个隔b分钟发射一次,每次发射的烟花可以持续 m + 1分钟,求同一时刻能够看到的最大烟数量
思路:
假设从0时刻同时发射即可两个烟花发射器同时发射,那答案就是在 m 分钟之内发射的烟花数量总数;a和b必定存在一个最小公倍数(必定存在某一个时刻会同时发射)
package cf.contest.R935;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
public class B {
private static InputStream is = System.in;
private static final BufferedReader in = new BufferedReader(new InputStreamReader(is));
private static final PrintWriter out = new PrintWriter(System.out);
static void solve() throws IOException {
String[] ins = pStringArray();
long a = pLong(ins[0]), b = pLong(ins[1]), m = pLong(ins[2]);
out.println(2 + m / a + m / b); // 2 是 0 时刻同时发射的烟花数量, m / a 是在第一个烟花发射后的m间隔内发射的烟花数量
}
public static void main(String[] args) throws IOException {
int t = 1;
t = Integer.parseInt(in.readLine());
while (t -- > 0) {
solve();
}
in.close();
out.flush();
out.close();
}
private static int pInt(String s) {
return Integer.parseInt(s);
}
private static long pLong(String s) {
return Long.parseLong(s);
}
private static String[] pStringArray() {
try {
return in.readLine().split(" ");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
C. Left and Right Houses
题目:
在村庄的一行房子之间造一条路,这条路将村庄分为左右两部分,每个房子的居民想分到左边
a
i
=
0
a_i=0
ai=0 或者右边
b
i
=
1
b_i=1
bi=1,要求每一部分需要满足该部分的有一半人以上(二分之一的上取整)是想分到该部分的,同时这条路应该靠近村庄的中心,输出路后面的房子的下标。
思路:
遍历,记录满足条件的答案即可(看代码注释)
package cf.contest.R935;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
public class C {
private static InputStream is = System.in;
private static final BufferedReader in = new BufferedReader(new InputStreamReader(is));
private static final PrintWriter out = new PrintWriter(System.out);
static void solve() throws IOException {
int n = pInt(in.readLine());
String s = in.readLine();
int a = 0;
// 先统计0和1的个数
for (int i = 0; i < n; i ++) {
if (s.charAt(i) == '0') {
a ++;
}
}
int b = n - a;
// cnt 为从0到i的0的个数
int cnt = 0, ans = 0;
// min 是当前ans距离村中心的距离
double min = Double.MAX_VALUE;
for (int i = 0; i <= n; i ++) {
int left = i, right = n - i;
// 两部分的居民有二分之一以上的村民想分到对应的部分
if ((left + 1) / 2 <= cnt && (right + 1) / 2 <= b - (i - cnt)) {
if (Math.abs(n / 2.0 - i) < min) {
ans = i;
min = Math.abs(n / 2.0 - i);
}
}
if (i < n && s.charAt(i) == '0') cnt ++;
}
out.println(ans);
}
public static void main(String[] args) throws IOException {
int t = 1;
t = Integer.parseInt(in.readLine());
while (t -- > 0) {
solve();
}
in.close();
out.flush();
out.close();
}
private static int pInt(String s) {
return Integer.parseInt(s);
}
private static long pLong(String s) {
return Long.parseLong(s);
}
private static String[] pStringArray() throws IOException {
return in.readLine().split(" ");
}
}
D. Seraphim the Owl
题目:
一队人排队,每个人都有对应的
a
i
a_i
ai 值和
b
i
b_i
bi 值;现在A处于最后面,他想到达前
m
m
m 个人的位置,每次他(当前位置
k
k
k)可以选择一个在他前面(位置
i
i
i) 的人交换位置,付给他
a
i
a_i
ai个硬币,同时给位置
i
i
i 和
k
k
k 之间的人(
i
<
j
<
k
i \lt j \lt k
i<j<k)全部付给
b
j
b_j
bj个硬币,求A到达前
m
m
m 个人所需要最小所需要的硬币数;
思路:
假设到达前m个人时最后的位置为x,如果直接交换(不和其他的人交换)那么需要付出的硬币数为
i
∈
(
x
,
n
]
i \in (x, n]
i∈(x,n] 区间的
b
i
b_i
bi 值加上
a
x
a_x
ax;此时最优解就是从
[
1
,
m
]
[1, m]
[1,m] 最小值,用维护 b
的前缀和 sum
求出
s
u
m
[
n
]
−
s
u
m
[
x
]
+
a
[
x
]
sum[n] - sum[x] + a[x]
sum[n]−sum[x]+a[x] 的最小值
c
o
s
t
cost
cost;
如果从需要在
[
m
+
1
,
n
]
[m + 1, n]
[m+1,n] 区间内选交换的人,如何使得
c
o
s
t
cost
cost 更小呢?
假设选取的人为位置
y
y
y,那么最终答案为
c
o
s
t
−
b
[
y
]
+
a
[
y
]
=
c
o
s
t
+
a
[
y
]
−
b
[
y
]
cost - b[y] + a[y] = cost + a[y] - b[y]
cost−b[y]+a[y]=cost+a[y]−b[y],因为
c
o
s
t
cost
cost 值是固定的,那么要找
a
[
y
]
−
b
[
y
]
<
0
a[y] - b[y] < 0
a[y]−b[y]<0 才能使得
c
o
s
t
cost
cost 变小,因此遍历
y
∈
[
m
+
1
,
n
]
y \in [m+1,n]
y∈[m+1,n] 区间找到
a
[
y
]
−
b
[
y
]
<
0
a[y] - b[y] < 0
a[y]−b[y]<0 加上即可
package cf.contest.R935;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
public class D {
static void solve() throws IOException {
String[] ins = pStringArray();
int n = pInt(ins[0]), m = pInt(ins[1]);
int[] a = pIntArray(1), b = pIntArray(1);
long[] sum = new long[n + 1];
// 前缀和
for (int i = 1; i <= n; i ++) {
sum[i] = sum[i - 1] + b[i];
}
// 找到 [1, m] 区间内的最小值
long preMin = Long.MAX_VALUE;
int pos = -1;
for (int i = 1; i <= m; i ++) {
if (sum[n] - sum[i] + a[i] < preMin) {
preMin = sum[n] - sum[i] + a[i];
pos = i;
}
}
long ans = sum[n] - sum[pos] + a[pos];
// 找 [m + 1, n] 区间内 a[i] - b[i] < 0 的
for (int i = m + 1; i <= n; i ++) {
int dis = a[i] - b[i];
if (dis <= 0) {
ans += dis;
}
}
out.println(ans);
}
public static void main(String[] args) throws IOException {
int t = 1;
t = Integer.parseInt(in.readLine());
while (t -- > 0) {
solve();
}
in.close();
out.flush();
out.close();
}
private static InputStream is = System.in;
private static final BufferedReader in = new BufferedReader(new InputStreamReader(is));
private static final PrintWriter out = new PrintWriter(System.out);
private static int pInt(String s) {
return Integer.parseInt(s);
}
private static long pLong(String s) {
return Long.parseLong(s);
}
private static String[] pStringArray() throws IOException {
return in.readLine().split(" ");
}
private static int[] pIntArray(int start) throws IOException {
String[] s = pStringArray();
int[] arr = new int[start + s.length];
for (int i = start, j = 0; i < arr.length; i++, j ++) {
arr[i] = Integer.parseInt(s[j]);
}
return arr;
}
}
E. Binary Search
题目:
给一个n长度的排列p,需要找到x,至多交换两次排列p中的元素,使得二分查找能够查找到x;输出交换的元素对
思路:
假设二分查找最后的数为 y
,那么必定满足 y < x
,存在两种情况:
- 二分查找的路径没有经过
mid
,那么交换x
和y
并不会影响判断,交换后最后一次判断p[mid] = x <= x
的结果肯定不变 - 二分查找的路径经过
mid
,那么交换前x
所处的位置肯定满足x = p[mid] <= x
,交换之后变成y = p[mid] <=x
结果并不会变化
因此只需要执行一次二分查找,然后直接将 x
和二分查找到的位置进行交换即可
package cf.contest.R935;
import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
public class E {
// 题目要求的二分查找
private static int binarySearch(int[] p, int x) {
int l = 1, r = p.length;
while (r - l > 1) {
int mid = l + r >> 1;
if (p[mid] <= x) {
l = mid;
} else {
r = mid;
}
}
return l;
}
// 数组的交换元素
private static void swap(int[] p, int i, int j) {
int tmp = p[i];
p[i] = p[j];;
p[j] = tmp;
}
static void solve() throws IOException {
String[] ins = pStringArray();
int n = pInt(ins[0]), x = pInt(ins[1]);
int[] p = pIntArray(1);
// 找到 x 当前的位置
int pos = -1;
for (int i = 1; i <= n; i ++) {
if (x == p[i]) {
pos = i;
break;
}
}
int idx = binarySearch(p, x);
out.println(1);
out.println(idx + " " + pos);
}
public static void main(String[] args) throws IOException {
int t = 1;
t = Integer.parseInt(in.readLine());
while (t -- > 0) {
solve();
}
in.close();
out.flush();
out.close();
}
private static InputStream is = System.in;
static {
try {
is = Files.newInputStream(Paths.get("F:\\Notes\\Algorithm\\Problems\\java\\java\\src\\main\\java\\input.txt"));
} catch (Exception e) {
is = System.in;
}
}
private static final BufferedReader in = new BufferedReader(new InputStreamReader(is));
private static final PrintWriter out = new PrintWriter(System.out);
private static int pInt(String s) {
return Integer.parseInt(s);
}
private static long pLong(String s) {
return Long.parseLong(s);
}
private static String[] pStringArray() throws IOException {
return in.readLine().split(" ");
}
private static int[] pIntArray(int start) throws IOException {
String[] s = pStringArray();
int[] arr = new int[start + s.length];
for (int i = start, j = 0; i < arr.length; i++, j ++) {
arr[i] = Integer.parseInt(s[j]);
}
return arr;
}
}