目录
A - Don’t Try to Count
题目描述
给你一个长度为n
的字符串和S1
和一个长度为m
的字符串S2
,你可以多次对S1
进行追加操作,即S1 = S1 + S1
,求最少次数让S2
作为S1
的字串,如果无法满足,输出-1
。
思路:模拟
- 使用
for
循环不好判断终止条件,使用while
更好,不容易超时。
public static void solve() throws Exception {
int n = readInt(), m = readInt();
String s1 = readString(), s2 = readString();
int res = 0;
// res = 0是因为当 s2不是 s1的字串时,可以考虑让 s1至少追加一次,如 s1="abcdef", s2="fa"
// s1.length() < 2 * m是因为最多只需要让 s1的字符串长度 s1.length() >= 2 * s2.length()
while (s1.indexOf(s2) == -1 && (res == 0 || s1.length() < 2 * m)) {
s1 += s1;
res++;
}
if (s1.indexOf(s2) == -1) {
printWriter.println("-1");
} else {
printWriter.println(res);
}
}
B - Three Threadlets
题目描述
给你3
根线和一把剪刀,你最多可以使用三次剪刀,使得所有线的长度相等且都是整数,问是否可以满足,如果可以输出YES
,否则输出NO
。
思路:排序+模拟
- 只需要对长度较长的两根线进行操作,不断进行最多三次操作,看是否可以满足三线长度相等。
public static void solve() throws IOException{
int[] arr = utils.nextIntArray(3);
Arrays.sort(arr, 1, 4);
boolean f = false;
int i = 1;
while (i <= 3) {
if (arr[2] != arr[1]) {// 继续切第二根线或者切了无法满足
arr[2] -= arr[1];
} else if (arr[3] != arr[1]) {// 切第三根线或者切了也无法满足
arr[3] -= arr[1];
}
i++;
}
if (arr[1] == arr[2] && arr[1] == arr[3]) {// 三线相等
printWriter.println("YES");
} else {
printWriter.println("NO");
}
}
C - Perfect Square
题目描述
给你一个 n ∗ n n * n n∗n的字符矩阵,你可以对矩阵中的字符进行以下操作:让该字符等于字母表的下一个字符。请你求出最少操作数,使得该矩阵等于该矩阵旋转 90 90 90度后的矩阵。
思路:矩阵模拟
- 只需要求出对于一个字符,其能旋转到的四个位置上的最大字符即可,例如坐标 [ i , j ] [i,j] [i,j]上的字符可以旋转到的位置有 [ i , j ] , [ j ] [ n − i + 1 ] , [ n − i + 1 ] [ n − j + 1 ] , [ n − j + 1 ] [ i ] [i, j], [j][n - i + 1], [n - i + 1][n - j + 1], [n - j + 1][i] [i,j],[j][n−i+1],[n−i+1][n−j+1],[n−j+1][i]。那么只需要求出这四个位置的最大字符即可。
public static void solve() throws IOException {
int n = readInt();
char[][] map = utils.nextCharArray(n, n);
long ops = 0;
boolean[][] vis = new boolean[n + 1][n + 1];
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= n; j++) {
if (!vis[i][j]) {// 防止重复遍历
char ch1 = map[i][j], ch2 = map[j][n - i + 1], ch3 = map[n - i + 1][n - j + 1], ch4 = map[n - j + 1][i];
int cur = Math.max(ch1, Math.max(ch2, Math.max(ch3, ch4)));
ops += (cur - ch1) + (cur - ch2) + (cur - ch3) + (cur - ch4);
vis[i][j] = true;
vis[j][n - i + 1] = true;
vis[n - i + 1][n - j + 1] = true;
vis[n - j + 1][i] = true;
}
}
}
printWriter.println(ops);
}
D - Divide and Equalize
题目描述
给你一个长度为n
的数组arr
,你可以对他进行以下操作:
- 选中两个坐标 i , j i,j i,j,然后选择 a i a_i ai的一个约数
x
,让 a i = a i x , a j = a j ∗ x a_i = \frac{a_i}{x},a_j = a_j * x ai=xai,aj=aj∗x
问是否可以通过一定的次数,使得
arr
的所有数相等。
思路:二分答案+大整数
- 可以先让所有的 a i a_i ai选中的约数为他本身,那么 a j = a j ∗ a i a_j = a_j * a_i aj=aj∗ai,最后会有 n − 1 n - 1 n−1个数为
1
,一个数p
为所有数的乘积。- 此时只需要判断数
p
是否可以拆分为&n&个相等数的成绩即可,如: [ 1 , 1 , 1000 ] [1,1,1000] [1,1,1000],最后可以操作为 [ 10 , 10 , 10 ] [10,10,10] [10,10,10]。
public static void solve() throws IOException{
int n = readInt();
BigInteger sum = BigInteger.ONE;
for (int i = 1; i <= n; i++) {
int p = readInt();
sum = sum.multiply(new BigInteger(p + ""));
}
long l = 0, r = (long) 1e6 + 5;
BigInteger s = sum;
while (l + 1 < r) {
long mid = l + r >> 1;
BigInteger cur = check(mid, n);
if (cur.compareTo(s) == 0) {
printWriter.println("YES");
return;
} else if (cur.compareTo(s) < 0) {
l = mid;
} else {
r = mid;
}
}
printWriter.println("NO");
}
public static BigInteger check(long x, int n) {
BigInteger cur = cur = new BigInteger(x + "").pow(n);
return cur;
}
E - Block Sequence
题目描述
给你一个长度为n
的数组arr
,它可以拆分为多个块,每个块的第一个元素为块的元素个数,然后该块后面的元素为这个块的所有元素,这样的数组被称为完美序列。你可以删除arr
中任意元素,使得arr
可以拆分为多个块,求这个最小操作数。
思路:动态规划
- 从后往前dp,枚举每个位置删与不删,如果对于第
i
个不满足位置 i + a r r [ i ] + 1 ≤ n + 1 i + arr[i] + 1 \leq n + 1 i+arr[i]+1≤n+1,那么这个位置只能删,否则,可以取该位置删与不删的最小值。- dp数组定义:从 i~n需要达到完美序列的最小操作次数。
public static void solve() throws IOException{
int n = readInt();
int[] arr = utils.nextIntArray(n);
// dp数组定义:从 i~n需要达到完美序列的最小操作次数
int[] dp = new int[n + 10];
dp[n + 1] = 0;
for (int i = n; i >= 1; i--) {
dp[i] = dp[i + 1] + 1;// 删
if (i + arr[i] + 1 <= n + 1) {// 不删
dp[i] = Math.min(dp[i], dp[i + arr[i] + 1]);// 在删与不删中取最大值
}
}
printWriter.println(dp[1]);// 1~n需要达到完美序列的最小操作次数
}