忙里偷闲当了次出题人,记录一下这次经历。先晒一下我优秀的学妹学弟们~ 参赛基本都是大一大二的。
题解ppt:校赛题解
A 参赛人数
Description
alpha想知道本次比赛的实际参赛人数。他想了个办法,看一下签到题有多少人ac,这不就行了嘛!
但是他怕签到题不简单,这样就无法统计到准确的人数。他为此很苦恼。
如果你觉得这道题很简单,也不想让alpha继续困扰下去,就输出”tjdl!”,去告诉alpha,这道题“太简单了!”
Input
无输入
Output
tjdl!
Sample Input
Sample Output
tjdl!
赛前阎老师一再嘱咐我题目不要出太难,于是诞生了这道题。
这题不会的同学们是认真的吗。。
#include <stdio.h>
int main()
{
printf("tjdl!\n");
return 0;
}
B 积木游戏
Description
beta喜欢玩积木游戏,游戏的规则是这样的:
最初有n个积木堆,各堆有自己的初始高度。游戏通过有限次数的移动,一次移动一个积木,最终使所有堆的高度相同。
beta想知道要达到目标需要的最少移动次数,他请来了精通程序设计的你来帮他算一算。
Input
输入包含多组数据,以0作为输入结束。
每组数据包含两行:
第一行为一个正整数n,表示积木堆的个数
第二行为n个正整数,h1 h2 … hn ,为每堆初始的积木个数。
数据规模约定:
1≤n≤50,1≤hi≤100
Output
对于每一组输入数据,输出将全部堆变为相同高度最少的移动次数。
在每组输出结果之间输出一个空行。
Sample Input
6
5 2 4 1 7 5
2
1 3
0
Sample Output
5
1
More Info
在第一组样例输入中:
第1堆积木拿1个到第2堆;
第5堆积木拿1个到第2堆;
第5堆积木拿1个到第4堆;
第5堆积木拿1个到第4堆;
第6堆积木拿1个到第4堆;
即最少需5次操作。
在第二组样例输入中:
第2堆积木拿1个到第1堆;
即最少需1次操作。
送分题
题目要求移动积木使高度相同。
先求出所有高度平均值avg,再遍历全部高度:如果当前高度h>avg,则累加差值进答案。
#include <bits/stdc++.h>
using namespace std;
int a[55];
int main()
{
int n, sum, avg;
while(scanf("%d", &n) && n) {
sum = avg = 0;
for(int i = 0; i < n; i++){
scanf("%d", &a[i]);
sum += a[i];
}
avg = sum / n;
sum = 0;
for(int i = 0; i < n; i++)
if(a[i] > avg)
sum += (a[i]-avg);
printf("%d\n", sum);
}
return 0;
}
C 矩阵求和
Description
我们定义这样n*n的矩阵:
1/1
1/1 1/2
1/2 1/1
1/1 1/2 1/3
1/2 1/1 1/2
1/3 1/2 1/1
…
相信你已经发现规律了:此类矩阵都是主对角线为1的对称矩阵,且主对角线两边元素的分母递增。
对于一个n*n的矩阵,请求出这个矩阵各元素的和。
Input
输入包含多组测试数据。每行给定整数n(n<50000),表示矩阵的阶数为n。
当n=0时,输入结束。
Output
对于每组输入数据,输出矩阵各元素的和,结果保留2位小数。
Sample Input
1
2
3
4
0
Sample Output
1.00
3.00
5.67
8.83
送分题2
题目要求计算一个简单对称矩阵的元素和。
找规律即可。
#include <stdio.h>
int main()
{
int n;
while(scanf("%d", &n) && n) {
double sum = 0.0;
for(int i = 2; i <= n; i ++)
sum += 1.0 / i * (n-i+1) * 2;
printf("%.2f\n", sum + n);
}
return 0;
}
D EOF
Description
Rio一直觉得程序设计里有些缩写很酷。
比如End Of File可以缩写成EOF,就比eof酷。
他经过研究,发现这些各首字母大写形式的组合,其实都很酷。
Input
输入的第一行是一个整数T,表示一共有T组测试数据。
每组数据占一行,包含不多于20个单词,每两个单词之间由空格隔开。
规定:
每个单词长度不超过20,单词仅包含大、小写字母。
Output
每组测试数据输出其相应的缩写形式,每组输出占一行。
Sample Input
2
End Of File
input output system
Sample Output
EOF
IOS
没有字符串如何ACM?
新手可能不太好处理这种一行数字,一行字符串的输入内容。
C语言处理时往往通过 getchar()
吃掉数字后面的回车,或者用scanf(“%d\n”, &T)
来接收数字。
如果忘记65、97、32(差值)这些常用的ascii码值,可以直接使用’a’、’A’、’A’-’a’进行计算。
此外,gets是不被建议使用的危险函数,在PAT等考试中已被禁止使用,但在蓝桥杯、ACM等竞赛中仍可使用。
C版本:
#include <bits/stdc++.h>
using namespace std;
char ss[250];
int main()
{
int T;
scanf("%d", &T);
getchar();
while(T --)
{
gets(ss);
for (int i = 0; ss[i]; i ++) {
if (!i || ss[i-1] == ' ') {
if (ss[i] >= 'a')
ss[i] -= 32;
printf("%c", ss[i]);
}
}
puts("");
}
return 0;
}
C++版本:
#include <bits/stdc++.h>
using namespace std;
int main()
{
int T;
string str, temp;
cin >> T;
cin.ignore();
while(T --)
{
getline(cin, str);
stringstream ss(str);
while (ss >> temp) {
if (temp[0] >= 'a') temp[0] -= 32;
cout << temp[0];
}
cout << endl;
}
return 0;
}
E 整数分解
Description
把一个正整数N分解成3个各不相同的正整数之和,并且要求每个正整数都不包含数字2和4,一共有多少种不同的分解方法?
注:不包含2和4指整数的各个位上的数都不为2且不为4。
Input
第一行一个整数T,表示输入有T组。
对于每一组输入数据,输入为一个正整数N。(3 < N < 2020)
Output
对每组输入数据,输出一个正整数,表示分解方法。
Sample Input
3
8
9
12
Sample Output
0
1
2
More Info
对样例的解释:
8没有满足要求的分解。
9分解为:1、3、5。
12分解为:1、3、8或1、5、6。
这题不是去年蓝桥杯填空原题么。。很难吗。。众大佬数论都用上了??
本题改编自2019蓝桥杯B组省赛填空题第四题。题目要求把一个正整数分解成3个各不相同的正整数之和,并且要求每个正整数都不包含数字2和4
如果使用如下形式的三层循环暴力搜索,时间达到2000^3导致超时,因此需要一些优化。
for (int i = 1; i < N; i ++)
for (int j = i+1; j < N; j ++)
for (int k = j+1; k < N; k ++)
if (i + j + k == N && Check(i) && Check(j)&& Check(k))
优化可以是:在已经确定两个数i、j后,第三个数用N-i-j表示则可以节省一层循环。
#include <stdio.h>
using namespace std;
//检查数字num是否包含2或4
bool Check(int num)
{
while(num)
{
if (num % 10 == 2 || num % 10 == 4)
return false;
num /= 10;
}
return true;
}
int main()
{
int T, N;
scanf("%d", &T);
while (T --){
scanf("%d", &N);
int ans = 0;
for (int i = 1; i < N; ++i)
if (Check(i))
for (int j = i + 1; N - i - j > j; ++j)
if (Check(j) && Check(N - i - j))
ans++;
printf("%d\n", ans);
}
return 0;
}
F 围棋程序
Description
小A喜欢围棋,这天他心血来潮想要设计一个围棋小游戏,但是一个基本的问题是:该如何计算闭合线段围成的面积呢?
比如在下面这个5*5的由“0 1”组成棋盘中,由“1”围出来的“0”的面积是:3。
0 0 0 0 0
0 1 1 1 1
0 1 0 1 0
1 0 0 1 1
1 1 1 1 0
规定:面积的计算方法是统计“1”所围成的闭合曲线中水平线和垂直线交点的数目,特别地,在边上的0不被计算,如第3行第5列的“0”不算作面积。
Input
一个10*10的棋盘矩阵,棋盘中每个点由0、1组成。
Output
由题意输出1围成的0的面积。
Sample Input
0 0 0 0 0 0 0 0 0 1
0 0 0 0 0 0 0 0 0 1
0 0 0 1 1 1 0 0 0 0
0 0 0 1 0 0 1 1 1 0
0 0 0 0 1 1 0 0 0 1
0 0 0 0 0 1 0 0 1 0
0 0 0 1 1 0 0 1 0 0
0 0 0 1 0 0 0 1 0 0
0 0 0 0 1 1 1 0 0 0
0 0 0 0 1 1 1 0 0 0
Sample Output
12
搜索。
直接截图题解ppt
#include <bits/stdc++.h>
using namespace std;
const int maxn = 15;
int a[maxn][maxn];
int Next[4][2] = {0,1,0,-1,1,0,-1,0};
bool Check(int x ,int y) {
return x >= 1 && y >= 1 && x <= 10 && y <= 10 && !a[x][y];
}
void dfs(int x, int y)
{
a[x][y] = 2;
for (int k = 0; k < 4; k ++) {
int nx = x + Next[k][0];
int ny = y + Next[k][1];
if (Check(nx, ny))
dfs(nx, ny);
}
}
int main() {
for (int i = 1; i <= 10; i ++)
for (int j = 1; j <= 10; j ++)
cin >> a[i][j];
// 从边界的0开始搜索
for (int i = 1; i <= 10; i ++) {
if(!a[i][1]) dfs(i, 1);
if(!a[i][10]) dfs(i, 10);
if(!a[1][i]) dfs(1, i);
if(!a[10][i]) dfs(10, i);
}
int ans = 0;
for (int i = 1; i <= 10; i ++)
for (int j = 1; j <= 10; j ++)
if(!a[i][j]) ans++;
cout << ans << endl;
return 0;
}
G 搬家
Description
要说我校的搬家史那可有的说的了。就alpha来说,他这个暑假刚经历一次搬寝室。
alpha寝室里的东西有n件,但搬东西真的是件很累的事,他只准备随便搬2m件就行。
alpha每趟左手、右手分别拎一件物品。
凭借天资聪颖,他迅速地发现了:每搬一趟,他的疲劳度与左、右手的物品的重量差的平方成正比。例如他左手拿重量为3的物品,右手拿重量为6的物品,则他搬完这次的疲劳度为(6-3)^2 = 9.
他想知道他搬完这2m件物品后疲劳度最低是多少?
Input
每组输入数据有两行。
第一行有两个数n,m (2*m ≤ n < 2000)。
第二行有n个整数分别表示n件物品的重量(重量是一个小于215的正整数)。
0 0表示输入结束。
Output
对于每组输入数据,输出一个正整数,表示他最少的疲劳度,每组输出占一行。
Sample Input
5 1
18467 6334 26500 19169 15724
7 1
29358 26962 24464 5705 28145 23281 16827
0 0
Sample Output
492804
1399489
出之前确实想过dp的题有爆0的可能,然后真的爆0了。
本题选自hdu1421,题目要求从n件物品中选k对,要求每对差平方之和最小。
基于贪心的思想,相邻的数差值肯定比不相邻的小,所以要先排序。
dp思想:dp[i][j]
表示从i个数中取j对物品时的最小值
取第i个物品: dp[i][j]=dp[i-2][j-1]+(v[i]-v[i-1])*(v[i]-v[i-1])
不取第i个物品: dp[i][j]=dp[i-1][j]
状态转移方程:
dp[i][j]=min{dp[i-2][j-1]+(v[i]-v[i-1])*(v[i]-v[i-1]),dp[i-1][j]
#include<bits/stdc++.h>
using namespace std;
const int maxn = 2050;
const int INF = 0x3f3f3f3f;
int dp[maxn][maxn];
int a[maxn];
int main()
{
int n, k;
while (cin >> n >> k && n)
{
for (int i = 1; i <=n; i++)
cin >> a[i];
sort(a+1, a+n+1);
for (int i = 1; i <= n; i++)
for (int j = 1; j <= k; j++)
dp[i][j] = INF;
for (int i = 2; i <= n; i++)
for (int j = 1; 2*j<= i ; j++)
dp[i][j] = min(dp[i - 2][j - 1] + (a[i] - a[i - 1])*(a[i] - a[i - 1]), dp[i - 1][j]);
cout << dp[n][k]<<endl;
}
return 0;
}
H 社团活动
Description
bbq的工作是管理学校的社团活动,具体来说是为每个社团活动分配教室。要把有限的教室合理安排给这些社团,是不容易的。
每个社团活动用k, t1, t2来表示:该社团活动在第t1天~第t2天内需要k个教室(包括t1,t2)。
bbq总是按社团活动申请的先后顺序分配教室,如果某一天剩余的教室数量不够满足某社团的要求,则停止教室的分配。bbq需要告知该社团,他们的该次社团活动无法进行。
Input
每组输入数据第一行包括两个正整数n、m,为总天数和社团活动的总数量。
第二行包含n个正整数,其中第i个数为ri,表示第i天空教室的数量。
接下来有m行,每行为一个社团活动的信息,包含三个正整数k, t1, t2。(k, t1, t2如题目描述)。
规定:
天数与社团活动编号均用从1开始的整数编号。
1≤n, m≤106,0≤ri≤109,0≤k≤109 ,1≤t1≤t2≤n。
Output
如果所有社团的申请均可满足,则输出0。
否则输出一个正整数,为需要bbq告知的活动无法进行的社团活动编号。
Sample Input
4 3
2 5 4 3
2 1 3
3 2 4
4 2 4
Sample Output
2
其实本来数据是卡线段树的,为了对背模版的同学一点奖励,我特意把卡常的数据删了,然后就真的被大一学妹用线段树A出来了,厉害呀。
题目大意:给n个数,做区间修改(减法),问第一个为负的是哪一位。
问题具有单调性(第k个活动无法满足,k或k前面的活动一定无法满足)。
所以二分答案即可。check函数的处理上用了查分数组,对于区间[l,r],要修改的值为d,则令sum[l]-=d,令sum[r+1]+=d即可。
此外,用线段树维护最小值是没问题的。
#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+50;
using ll = long long;
int n,m;
ll a[N],room[N],sum[N],t1[N],t2[N];
bool check(int mid)
{
memset(sum,0,sizeof(sum));
for (int i=1;i<=mid;i++)
{
sum[t1[i]]-=room[i];
sum[t2[i]+1]+=room[i];
}
int cnt=0;
for (int i=1;i<=m;i++)
{
cnt+=sum[i];
if (a[i]+cnt < 0) return 0;
}
return 1;
}
int main()
{
scanf("%d%d",&n,&m);
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
for (int i = 1; i <= m; i++)
scanf("%lld%lld%lld", &room[i],&t1[i],&t2[i]);
int l=1, r=m+1;
while(l < r)
{
int mid = (l + r) >> 1;
if (check(mid)) l = mid+1;
else r = mid;
}
if (l == m+1) {printf("0\n");return 0;}
printf("%d\n",l);
return 0;
}
最后,祝我校ACM事业蒸蒸日上、学弟学妹明年再创佳绩!