Week1
Acwing 寒假每日一题,第一周全部题目详解
2022/12/26
01 题目描述
# 题目描述
Farmer John 最近购入了 N 头新的奶牛,每头奶牛的品种是更赛牛(Guernsey)或荷斯坦牛(Holstein)之一。
奶牛目前排成一排,Farmer John 想要为每个连续不少于三头奶牛的序列拍摄一张照片。
然而,他不想拍摄这样的照片,其中只有一头牛的品种是更赛牛,或者只有一头牛的品种是荷斯坦牛——他认为这头奇特的牛会感到孤立和不自然。
在为每个连续不少于三头奶牛的序列拍摄了一张照片后,他把所有「孤独的」照片,即其中只有一头更赛牛或荷斯坦奶牛的照片,都扔掉了。
给定奶牛的排列方式,请帮助 Farmer John 求出他会扔掉多少张孤独的照片。
如果两张照片以不同位置的奶牛开始或结束,则认为它们是不同的。
# 输入格式
输入的第一行包含 N。
输入的第二行包含一个长为 N 的字符串。如果队伍中的第 i 头奶牛是更赛牛,则字符串的第 i 个字符为 G。否则,第 i 头奶牛是荷斯坦牛,该字符为 H。
# 输出格式
输出 Farmer John 会扔掉的孤独的照片数量。
# 输入样例:
5
GHGHG
# 输出样例:
3
02 思路
题目的意思就是,给定一个长度是N的字符串,在里面找到一个子串,这个子串满足如下要求:
- 子串的长度大于等于3
- 这个子串之中只能包含一个G或者一个H,也可以理解为:如果是一个G,其余的都是H,如果一个是H,其余的都是G
而具体的思路就是,枚举每一个位置,记录这个位置,左边和右边分别有多少个连续不同的字母,分别记为L和R,主要有如下三种情况:
最后将这三种情况加起来就行。所以最终的方案就是:L * R + (L - 1) + (R - 1),如果说这个L或者R是1,那么情况二或者情况三就是0,因为不满足长度是3的条件
在代码实现上,我们可以用一个变量来记录这个一段有多少个连续的H和多少个连续的G。
如果说当前位置是G,说明H就不连续的,H清零,G++,只需要从左扫一遍,记录左边有多个不同且连续的,在从右扫一遍,记录右边有多少个连续的记录
03 代码示例
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
String str = cin.next();
int[] l = new int[n]; // 记录对应位置左边,有多少个连续且不同的
int[] r = new int[n]; // 记录对应位置右边,有多个个连续且不同的
int h = 0; // 有多少个连续的H
int g = 0; // 有多少个连续的G
for (int i = 0; i < n; i++) {
if(str.charAt(i) == 'G') { // 如果说当前位置是G
l[i] = h;// 应该记录这个位置,左边有多少个连续的H
h = 0;// 从这里开始H就不连续了,所以H清空
g++; // G 仍然是连续的,并且连续的个数就加1
} else {
l[i] = g;
g = 0;
h++;
}
}
h = 0;
g = 0;
// 从右边往左边扫,记录
for (int i = n - 1; i >= 0; i--) {
if(str.charAt(i) == 'G') {
r[i] = h;
h = 0;
g++;
} else {
r[i] = g;
g = 0;
h++;
}
}
// 统计结果
long res = 0;
for (int i = 0; i < n; i++) {
res += (long)l[i] * r[i] + Math.max(l[i] - 1,0) + Math.max(r[i] - 1,0);
}
System.out.println(res);
}
}
2022/12/27
01 题目描述
# 题目描述
给定两个正整数 n 和 k,求从 1 到 n 这 n 个正整数的十进制表示中 k 出现的次数。
# 输入格式
共一行,包含两个整数 n 和 k。
# 输出格式
输出一个整数,表示答案。
02 思路
通过枚举每个数的每一位,判断是否是k,统计一下即可
03 代码示例
package 寒假每日一题;
import java.util.Scanner;
public class 统计次数 {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
int k = cin.nextInt();
int res = 0;
for (int i = 1; i <= n; i++) {
int tem = i;
while (tem != 0) {
int t = tem % 10;
if(t == k) res++;
tem /= 10;
}
}
System.out.println(res);
}
}
2022/12/28
01 题目描述
# 题目描述
有 N 堆石子,每堆的石子数量分别为 a1,a2,…,aN。你可以对石子堆进行合并操作,将两个相邻的石子堆合并为一个石子堆
我们希望通过尽可能少的操作,使得石子堆集合中的每堆石子的数量都相同。请你输出所需的最少操作次数。
本题一定有解,因为可以将所有石子堆合并为一堆。
# 输入格式
第一行包含整数 T,表示共有 T 组测试数据。
每组数据第一行包含整数 N。
第二行包含 N 个整数 a1,a2,…,aN。
# 输出格式
每组数据输出一行结果。
# 案例
6
1 2 3 1 1 1
结果是:3
3
2 2 3
结果是:2
5
0 0 0 0 0
结果是:0
02 思路
如果说整个石子的全部重量是sum,最终被分为了若干堆,如果说每堆的数量是cnt
- 则首先会有
sum % cnt == 0,cnt的可能取值,一定是sum的约数。 - 整个石子堆会被分为
sum / cnt堆,操作的次数就是:n - sum / cnt,要想让这个值最小,则cnt一定越大越好
并且,题目中要求只能合并相邻的两堆,则题目就可以理解为,将整个石子堆分为若干个连续段,并且每一段的数量是cnt,我们可以从前往后枚举每一段,比如说我这一段的数量是已经是s
- 如果说 s 加上当前这个点 > cnt,则表示不满足,这个cnt不符合要求
- 如果说s加上当前这个点 = cnt,说明合适,就可以枚举下一段了
03 代码示例
import java.util.Scanner;
public class Main {
private static boolean check(int cnt,int[] arr) {
int n = arr.length;
int s = 0;
for (int i = 0; i < n; i++) {
s += arr[i];
if(s > cnt) return false;
if(s == cnt) {
s = 0;
}
}
return true;
}
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int T = cin.nextInt();
while (T-- > 0) {
/*
* 数据的读取
* */
int n = cin.nextInt();
int[] arr = new int[n + 10];
int sum = 0;
for(int i = 0; i < n; i++) {
arr[i] = cin.nextInt();
sum += arr[i];
}
/**
* 这个枚举,整个石子序列将会被分为多少堆,分为了i堆,也就是进行了 n - i 次操作
* 从大到小枚举,就表示,i 越大,n - i就越小
* */
for (int i = n; i >= 0; i--) {
if (sum % i == 0 && check(sum / i,arr)) {
System.out.println(n - i);
break;
}
}
}
}
}
2022/12/29
01 题目描述
# 题目描述
北京大学对本科生的成绩施行平均学分绩点制(GPA)。既将学生的实际考分根据不同的学科的不同学分按一定的公式进行计算。
公式如下:
实际成绩 绩点
90——100 4.0
85——89 3.7
82——84 3.3
78——81 3.0
75——77 2.7
72——74 2.3
68——71 2.0
64——67 1.5
60——63 1.0
60以下 0
现要求你编写程序求出某人 A 的总评绩点(GPA)。
# 注意事项
- 一门课程的学分绩点 = 该课绩点 × 该课学分
- 总评绩点 = 所有学科学分绩点之和 / 所有课程学分之和
# 输入格式
- 第一行,总的课程数 n;
- 第二行,相应课程的学分(两个学分间用空格隔开);
- 第三行,对应课程的实际得分;
- 此处输入的所有数字均为整数。
# 输出格式
- 输出有一行,总评绩点,精确到小数点后 2 位小数。
02 思路
语法题
03 代码示例
import java.util.Scanner;
public class Main {
private static double get(int score) {
if (score >= 90) return 4;
if (score >= 85) return 3.7;
if (score >= 82) return 3.3;
if (score >= 78) return 3.0;
if (score >= 75) return 2.7;
if (score >= 72) return 2.3;
if (score >= 68) return 2.0;
if (score >= 64) return 1.5;
if (score >= 60) return 1.0;
return 0;
}
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
int[] f = new int[n + 10];
int score = 0;
// 学分
for(int i = 0; i < n; i++) {
f[i] = cin.nextInt();
score += f[i];
}
double course = 0;
for (int i = 0; i < n; i++) {
int tem = cin.nextInt();
course += get(tem) * f[i];
}
System.out.printf("%.2f",course / score);
}
}
2022/12/30
01 题目描述
# 题目描述
Farmer John 计划为奶牛们新开办一所大学!
有 N 头奶牛可能会入学。
每头奶牛最多愿意支付 ci 的学费。
Farmer John 可以设定所有奶牛入学需要支付的学费。
如果这笔学费大于一头奶牛愿意支付的最高金额,那么这头奶牛就不会入学。
Farmer John 想赚尽可能多的钱,从而可以给他的讲师提供一笔可观的工资。
请求出他能赚到的钱的数量,以及此时应当收取多少学费。
# 输入格式
输入的第一行包含 N。
第二行包含 N 个整数 c1,c2,…,cN,其中 ci 是奶牛 i 愿意支付的最高学费金额。
# 输出格式
输出 Farmer John 可以赚到的最大金额以及最优情况下他应该收取的学费。如果有多个解,输出收取学费最小的解。
注意这个问题涉及到的整数可能需要使用 64 位整数型(例如,Java 中的 “long”,C/C++ 中的 “long long”)。
02 思路
1)如果说值,取到1号线,左边三头牛,肯定没有办法入学,右边3头牛能够入学,并且取到2号线,也是能够符合这种情况,并且能够赚更多的钱。所以能够得出第一个结论,如果说我想取的值为x,并且f[i] < x <= f[j],i < j,此时x取f[j],也就是说这个x取的值,一定是数组中的值。
2)如果说取f[j],则赚的钱为f[j] * (n - j)
03 代码示例
import java.util.Arrays;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner cin = new Scanner(System.in);
int n = cin.nextInt();
long[] f = new long[n];
for(int i = 0; i < n; i++) f[i] = cin.nextInt();
Arrays.sort(f);
long money_total = 0; // 总共能赚多少钱
long money = 0; // 学费
for (int i = 0; i < n; i++) {
long tem = (long) (f[i] * (n - i)); // 当前这种情况之下,能够赚多少钱
if(tem > money_total) {
money_total = tem;
money = f[i];
}
}
System.out.println(money_total+" "+money);
}
}