【刷题日记】北京交通大学C语言积分赛第一轮题解

反思小结

因为感冒,状态实在太差了,A题做了几遍,两个小时才AC,问题出在对动态规划理解不够深刻,同时这方面题目刷得太少,导致实战思路不清晰。要多刷动态规划!
总体来看题目难度不大,只需要最简单的算法思维和数据结构就能完成。但速度还是不够快,对英文题面缺少理解能力(A题AI和翻译器都翻不明白,最后对着词典看了好久才看懂题目……)
题解按照我个人认为的难度排序。

B String Task

题目

Petya started to attend programming lessons. On the first lesson his task was to write a simple program. The program was supposed to do the following: in the given string, consisting if uppercase and lowercase Latin letters, it:

deletes all the vowels,
inserts a character "." before each consonant,
replaces all uppercase consonants with corresponding lowercase ones.
Vowels are letters "A", "O", "Y", "E", "U", "I", and the rest are consonants. The program's input is exactly one string, it should return the output as a single string, resulting after the program's processing the initial string.

Help Petya cope with this easy task.

Input
The first line represents input string of Petya's program. This string only consists of uppercase and lowercase Latin letters and its length is from 1 to 100, inclusive.

Output
Print the resulting string. It is guaranteed that this string is not empty.

Examples
Input
tour
Output
.t.r
Input
Codeforces
Output
.c.d.f.r.c.s
Input
aBAcAba
Output
.b.c.b

中文题面

给定一个字符串,去掉其中的元音字母,在辅音字母前加一个.,将大写辅音字母全部改为小写。

题解

一定要注意,这道题指的元音字母竟然包括Y!太阴险了!看到好多大佬也在这里错了,应该都是因为这个……

AC代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

string s;

int main(){
	cin >> s;
	for(auto c:s){
		if('A' <= c && c <= 'Z'){
			if(c != 'A' && c != 'E' && c != 'I' && c != 'O' && c != 'U' && c != 'Y'){
				cout << '.' << (char)(c+32);
			}
		}
		else{
			if(c != 'a' && c != 'e' && c != 'i' && c != 'o' && c != 'u' && c != 'y'){
				cout << '.' << c;
			}
		}
	}
	return 0;
}

D Grade Allocation

题目

n students are taking an exam. The highest possible score at this exam is m. Let ai be the score of the
i-th student. You have access to the school database which stores the results of all students.
You can change each student's score as long as the following conditions are satisfied:
All scores are integers 0≤ai≤m.
The average score of the class doesn't change.
You are student 1 and you would like to maximize your own score.
Find the highest possible score you can assign to yourself such that all conditions are satisfied.
Input
Each test contains multiple test cases.
The first line contains the number of test cases
t(1≤t≤200)-The description of the test cases follows.
The first line of each test case contains two integers
n and m(1≤n≤103)(1≤m≤105)-the number of students and the highest possible score respectively.
The second line of each testcase contains
n integers
a1,a2,…,an.(0≤ai≤mi) — scores of the students.
Output
For each testcase, output one integer — the highest possible score you can assign to yourself such that both conditions are satisfied._
Examples
Input
2
4 10
1 2 3 4
4 5
1 2 3 4
Output
10
5
Note
In the first case,a=[1,2,3,4], with average of 2.5. You can change array a to [10,0,0,0]. Average remains 2.5, and all conditions are satisfied.

中文题面

t个测试样例。
输入n,m。给定长度为n的自然数数列a,改变a,保持a的平均数不变,且每一项仍为不大于m的自然数,求改变后a的第一个元素最大值。

题解

从第二个元素及以后所有元素身上不断吸血就行了,直到榨干其他所有元素或者a[0]达到上限m。

AC代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

i64 t,n,m;
i64 a[1010];

int main(){
	scanf("%lld",&t);
	while(t--){
		scanf("%lld %lld",&n,&m);
		for(i64 i = 0;i<n;i++)	scanf("%lld",&a[i]);
		for(i64 i = 1;i<n;i++){
			a[0] += a[i];
			if(a[0] >= m){
				a[0] = m;
				break;
			}
		}
		printf("%lld\n",a[0]);
	}
	return 0;
}

C Assembly via Minimums

中文题面

t个测试点。(t最大200)
输入n。(n最大10^3)
对于有n个元素的数组a,其所有元素两两之间有min(a[i], a[j]),这些较小值组成新数组b。现给定新数组b,还原出一种可能的a。

测试样例

输入

5
3
1 3 1
2
10
4
7 5 3 5 3 3
5
2 2 2 2 2 2 2 2 2 2
5
3 0 0 -2 0 -2 0 0 -2 -2

输出

1 3 3
10 10
7 5 3 12
2 2 2 2 2
0 -2 0 3 5

题解

把a中的数从小到大排好序(下标从0开始),在b中,a[i]出现n-1-i次。
把给定的b从小到大排好序,每次跳过n-1-i个数输出就好了。鉴于a最大元素在b中没出现,认为和第二大的元素相等即可。

AC代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

i64 t,n,scan,now,now_add;
vector<i64>b;

int main(){
	scanf("%lld",&t);
	while(t--){
		b.clear();
		scanf("%lld",&n);
		for(i64 i = 0;i<n*(n-1)/2;i++){
			scanf("%lld",&scan);
			b.push_back(scan);
		}
		sort(b.begin(),b.end());
		now = 0, now_add = n-1;
		while(now_add){
			printf("%lld ",b[now]);
			now += now_add;
			now_add --;
		}
		printf("%lld\n",b[n*(n-1)/2-1]);
	}
	return 0;
}

E Power of Points

中文题面

t个测试用例。(t最大10^4)
给定一个长度为n的数列x,x[i]表示点集x的第i个元素在数轴上的位置。(所有测试用例n之和最大2*10^5 ,x[i]为从1到10^9的整数)
对于第i个答案,对于任意整数p,定义函数fp为:x中所有元素(包括x[i])与x[i]连成线段,覆盖p的线段数量,求∑fp 1<=p<=10^9。

测试样例

输入

3
3
1 4 3
5
1 2 5 7 1
4
1 10 100 1000

输出

8 7 6
16 15 18 24 16
1111 1093 1093 2893

题解

显然,对于第i个答案,要求的其实就是以x[i]为一个端点连成的所有线段总长度。
对x排序,用pair记录好原来的位置,用前缀和l_sum表示左边端点的坐标和,那么左边端点与x[i]的连线总长度(不考虑端点x[i])就是i*x[i] - l_sum,用后缀和(自己瞎起的名字)r_sum表示右边端点的坐标和,那么右边端点与x[i]的连线总长度(不考虑端点x[i])就是r_sum - (n-1-i)*x[i]

AC代码
//容易发现,其实就是计算所有线段长度之和
//排序后使用前缀和、后缀和(自己瞎编的名字嘻嘻)

#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

i64 t,n,scan,r_sum,l_sum,ans;
vector<pair<i64, i64>>num;
i64 anss[200100];

int main(){
	scanf("%lld",&t);
	while(t--){
		scanf("%lld",&n);
		num.clear();
		l_sum = r_sum = ans = 0;
		for(i64 i = 0;i<n;i++){
			scanf("%lld",&scan);
			r_sum += scan;
			num.push_back({scan, i});
		}
		sort(num.begin(),num.end());
		for(i64 i = 0;i<n;i++){		//i刚好用来表示该点左边的点的数量
			ans = 0;
			r_sum -= num[i].first;	//处理后缀和
			ans += r_sum - (n-1-i) * num[i].first;	//计算右侧线段长度
			ans += i * num[i].first - l_sum;		//计算左侧线段长度
			ans += n;								//弥补线段端点长度
			l_sum += num[i].first;	//处理前缀和
			anss[num[i].second] = ans;
		}
		for(i64 i = 0;i<n;i++)
			printf("%lld ",anss[i]);
		printf("\n");
	}
	return 0;
}

A Lazy Narek

中文题面

(原题描述有点难理解,我尽量翻译地简洁一点)
小明太懒了,让ChatGPT出题,它给出了n个长度为m的字符串,小明选出其中任意个(可以不选),保持顺序不变将这些字符串拼接在一块。然后从前往后开始找所有的n、a、r、e、k:刚开始需要n,找到n后需要a,以此类推,找出一组narek后小明加5分,并且这五个字母认为是“有效的”。
找的过程中遇到不需要的n、a、r、e、k,认为是无效的。最后哪怕需要,但是凑不成一组narek的n、a、r、e也认为是无效的。对于每个无效的字母,ChatGPT加1分。
小明想要适当选取字符串,使得自己的分数减ChatGPT的分数最大,求这个最大分差可以是多少。
t最大10^5 , n,m最大10^3, 所有测试样例的n*m之和最大10^6。

测试样例

输入

4
5 2
nn
aa
rr
ee
kk
1 5
narek
1 4
nare
5 7
nrrarek
nrnekan
uuuuuuu
ppppppp
nkarekz

输出

0
5
0
7

解释

对于第一个测试样例,五个字符串全选,其中第一次出现的n、a、r、e、k都有效,第二次出现的都无效(因为不需要),所以分数差为0.或者小明一个字符串都不选,分数差也为0.
对于第二个测试样例,选中这个字符串,5个字母都有效,没有无效字母,分数差为5.
对于第三个测试样例,选中这个字符串,4个字母都无效(需要,但凑不成一组),分数差为-4,不如不选,分数差为0.
对于第四个测试样例,选中第一个和第五个,组成字符串nrrareknkarekz,其中紫色为有效字母,红色为无效字母,黑色为不考虑字母。分数差为10-2=8。

题解

用dp[i]来记录目前为止以第i个开头的选项中,最大可以拿到多大分差。(没有这种情况出现,视为负无穷)

AC代码
#include <bits/stdc++.h>
using namespace std;
using i64 = long long;

i64 t,n,m,score,need;
const i64 INF = 0x3f3f3f3f;
i64 dp[5];		//dp[i]表示目前为止,以第i个字母为开始时可获得的最大分数
i64 dp2[5];		//备份
char s[1010];
char chs[] = "narek";

bool check(char c){
	return (c=='n' || c=='a' || c=='r' || c=='e' || c=='k');
}

int main(){
	scanf("%lld",&t);
	while(t--){
		scanf("%lld %lld",&n,&m);
		for(i64 i = 0;i<5;i++)	dp[i] = -INF;
		dp[0] = 0;
		for(i64 i = 0;i<n;i++){
			for(i64 j = 0;j<5;j++)	dp2[j] = dp[j];
			scanf("%s",s);
			//分五次更新dp
			for(i64 now = 0;now<5;now++){
				score = 0;
				need = now;
				//遍历字符串
				for(i64 p = 0;p<m;p++){
					//不需要的范围内单词,给chatgpt加分
					if(check(s[p]) && s[p] != chs[need]){
						score--;
					}
					//找到了需要的字母
					else if(check(s[p]) && s[p] == chs[need]){
						need++;
						score--;
						if(need == 5){
							need = 0;
							score += 10;
						}
					}
					//printf("nowchar:%c score:%lld\n",s[p],score);
				}
				//结算
				if(score + dp[now] > dp2[need])	dp2[need] = score + dp[now];
			}
			for(i64 j = 0;j<5;j++)	dp[j] = dp2[j];
			//printf("NOW:%lld %lld %lld %lld %lld\n",dp[0],dp[1],dp[2],dp[3],dp[4]);
		}
		i64 ans = 0;
		for(i64 i = 0;i<5;i++)	ans = max(ans, dp[i]);
		printf("%lld\n",ans);
	}
	return 0;
}
posted @ 2025-03-17 12:00  Alkaid16  阅读(17)  评论(0)    收藏  举报