TSGZ&NUAA寒假集训-周赛Round3
第一题:【XR-4】模拟赛
题目描述
X 校正在进行 CSP 前的校内集训。
一共有 n n n 名 OIer 参与这次集训,教练为他们精心准备了 m m m 套模拟赛题。
然而,每名 OIer 都有各自的时间安排,巧合的是,他们在接下来的 k k k 天中都恰好有 m m m 天有空打模拟赛。
为了方便管理,教练规定一个人必须按顺序打完 m m m 套模拟赛题。
比如,小 X 在接下来的第 2 , 3 , 5 2,3,5 2,3,5 天有空打模拟赛,那么他就必须在第 2 2 2 天打第 1 1 1 套模拟赛题,第 3 3 3 天打第 2 2 2 套模拟赛题,第 5 5 5 天打第 3 3 3 套模拟赛题。
教练需要为每一个人的每一次模拟赛做准备,为了减小工作量,如果在某一天有多个人打同一套模拟赛题,那么教练只需要在这一天准备一场使用这一套题的模拟赛即可。
你作为机房大佬,教练想请你帮他计算一下,他每天需要准备多少场模拟赛。
题意
一共有 n 名同学,教练为他们准备了 m 套模拟赛题。
他们在接下来的 k 天中都恰好有 m 天有空打模拟赛。必须按顺序打完 m 套模拟赛题。
比如,小 X 在接下来的第 2,3,5 天有空打模拟赛,那么他就必须在第 2 天打第 1 套模拟赛题,第 3 天打第 2 套模拟赛题,第 5 天打第 3 套模拟赛题。
教练需要为每一个人的每一次模拟赛做准备,如果在某一天有多个人打同一套模拟赛题,那么教练只需要在这一天准备一场使用这一套题的模拟赛即可。
计算一下,他每天需要准备多少场模拟赛。
输入格式
第一行三个整数 n , m , k n,m,k n,m,k。
接下来 n n n 行,每行 m m m 个整数,第 i i i 行第 j j j 列的整数 a i , j a_{i,j} ai,j 表示第 i i i 个人在接下来的 k k k 天中第 j j j 个有空的日子为第 a i , j a_{i,j} ai,j 天。
输出格式
一行 k k k 个整数,第 i i i 个整数表示接下来的第 i i i 天教练需要准备的模拟赛场数。
样例
样例输入
6 3 7
2 3 4
2 5 7
3 5 7
1 3 5
5 6 7
1 2 3
样例输出
1 2 3 1 3 1 1
输入格式
第一行三个整数 n,m,k。接下来 n 行,每行 m 个整数,第 i 行第 j 列的整数 a i,j,
表示第 i 个人在接下来的 k 天中第 j 个有空的日子为第 a i,j天。
输入格式
一行 k 个整数,第 i 个整数表示接下来的第 i 天教练需要准备的模拟赛场数。
思路
我们首先建立一个二维数组,接着只需要看每列是否出现该天数,出现则需要准备,而且每天无论出现几次都只准备一次。
代码
#include<bits/stdc++.h>
using namespace std;
int a[1005][1005],vis[1005][1005],x[1005];
int main()
{
int n,m,k;
cin>>n>>m>>k;
for(int i=1;i<=n;i++)//控制输入的行数
for(int j=1;j<=m;j++)
cin>>a[i][j];//作为二维数组去读入
for(int i=1;i<=n;i++)//控制输入的行数
{
for(int j=1;j<=m;j++)
{
if(!vis[j][a[i][j]])//每一列的元素中如果多个有相同的,
{//把多个相同的当作一个来计算
vis[j][a[i][j]]=1;//标记已出现过
x[a[i][j]]++;//对应在当天能打模拟赛的场数
}
}
}
for(int i=1;i<=k;i++)
cout<<x[i]<<" ";
return 0;
}
总结
简单模拟题,理清题目思路即可
第二题:日志分析
题目描述
M 海运公司最近要对旗下仓库的货物进出情况进行统计。目前他们所拥有的唯一记录就是一个记录集装箱进出情况的日志。该日志记录了两类操作:第一类操作为集装箱入库操作,以及该次入库的集装箱重量;第二类操作为集装箱的出库操作。这些记录都严格按时间顺序排列。集装箱入库和出库的规则为先进后出,即每次出库操作出库的集装箱为当前在仓库里所有集装箱中最晚入库的集装箱。
出于分析目的,分析人员在日志中随机插入了若干第三类操作――查询操作。分析日志时,每遇到一次查询操作,都要报告出当前仓库中最大集装箱的重量。
输入格式
包含 N + 1 N+1 N+1 行:
第一行为一个正整数 N N N,对应于日志内所含操作的总数。
接下来的 N N N 行,分别属于以下三种格式之一:
- 格式 1:
0 X
,表示一次集装箱入库操作,正整数 X X X 表示该次入库的集装箱的重量。 - 格式 2:
1
,表示一次集装箱出库操作,(就当时而言)最后入库的集装箱出库。 - 格式 3:
2
,表示一次查询操作,要求分析程序输出当前仓库内最大集装箱的重量。
当仓库为空时你应该忽略出库操作,当仓库为空查询时你应该输出 0 0 0。
输出格式
输出行数等于日志中查询操作的次数。每行为一个整数,表示查询结果。
样例 #1
样例输入 #1
13
0 1
0 2
2
0 4
0 2
2
1
2
1
1
2
1
2
样例输出 #1
2
4
4
1
思路
本题需要对仓库中集装箱进出情况的管理和查询。我们需要编写一个程序来模拟处理这些记录并回答查询操作时的最大集装箱重量。
1、使用栈来模拟先进后出的规则。
2、读取日志文件,逐行处理每个记录。对于入库操作,将集装箱重量入栈;对于出库操作,弹出栈顶元素。
3、遇到查询操作时,需要报告当前仓库中最大集装箱的重量。可以通过遍历栈中的元素找到最大值并报告。
4、最后,确保程序能正确处理各种情况,包括空栈和查询操作。
代码1
#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
int f[200001],n,x,y,t=0;
int main()
{
cin>>n;
f[0]=0;
for (int i=1; i<=n; i++)
{
cin>>x;
//如果是入库操作(x=0),读取集装箱重量y,更新当前仓库中最大集装箱的重量。
if (x==0)
{
cin>>y;
t++;
f[t]=max(f[t-1],y);
}
//如果是出库操作(x=1),如果仓库中有集装箱,则将仓库中最后入库的集装箱出库。
if (x==1) if (t!=0) t--;
//如果是查询操作(x=2),输出当前仓库中最大集装箱的重量。
if (x==2) cout<<f[t]<<endl;
}
return 0;
}
代码2
#include<bits/stdc++.h>
using namespace std;
//存储集装箱的重量
stack<int>a;
//存储当前仓库中最大集装箱的重量。
stack<int>b;
int n,m,x;
int main()
{
scanf("%d",&n);
while(n--)
{
scanf("%d",&m);
//入库
if(m==0)
{
scanf("%d",&x);
//将x入栈a,并更新当前仓库中最大集装箱的重量入栈b
a.push(x);
if(b.empty()||x>b.top())
b.push(x);
else b.push(b.top());
}
//出库
else if(m==1)
{
//分别从栈a和栈b中弹出栈顶元素。
a.pop();
b.pop();
}
//查询操作
else
{
//如果栈b为空,则输出0;否则输出栈b的栈顶元素。
if(b.empty()) printf("0\n");
else printf("%d\n",b.top());
}
}
return 0;
}
总结
编写一个程序来模拟处理这些记录并回答查询操作时的最大集装箱重量
第三题:Nearest Minimums
题面翻译
给定一个长度为n的整数数组,请找出最小值之间的最短距离。保证最小值在数组中至少出现2次。
题目描述
You are given an array of $ n $ integer numbers $ a_{0},a_{1},…,a_{n-1} $ . Find the distance between two closest (nearest) minimums in it. It is guaranteed that in the array a minimum occurs at least two times.
找到一个长度为n的序列中,距离最短的最小值的距离。
思路
1、遍历整个数组,找到最小值以及其在数组中的所有索引位置。
2、计算相邻两个最小值之间的距离,并记录最小距离。
3、最终得到的最小距离即为最小值之间的最短距离。
输入格式
The first line contains positive integer $ n $ ( $ 2<=n<=10^{5} $ ) — size of the given array. The second line contains $ n $ integers $ a_{0},a_{1},…,a_{n-1} $ ( $ 1<=a_{i}<=10^{9} $ ) — elements of the array. It is guaranteed that in the array a minimum occurs at least two times.
输出格式
Print the only number — distance between two nearest minimums in the array.
样例 #1
样例输入 #1
2
3 3
样例输出 #1
1
样例 #2
样例输入 #2
3
5 6 5
样例输出 #2
2
代码
#include<bits/stdc++.h>
using namespace std;
//存储输入的整数数组。
int a[100001];
//存储重复的最小值的位置。
int b[100001];
int n;
//记录重复的最小值的个数。
int c=1;
//记录数组中的最小值。
int k=1000000000;
//存储最小值之间的最短距离
int ans=100000;
int main()
{
cin>>n;
//遍历数组a,找到最小值k
for(int i=1;i<=n;i++)
{
cin>>a[i];
if(k>a[i])
k=a[i];
}
//遍历数组a,记录重复的最小值的位置到数组b中。
for(int i=1;i<=n;i++)
{
if(a[i]==k)
b[c++]=i;
}
for(int i=2;i<c;i++)
{
//遍历数组b,计算相邻两个最小值之间的距离,并更新最短距离ans。
if(ans>b[i]-b[i-1])
ans=b[i]-b[i-1];
}
cout<<ans;
}
第四题:【深基15.例2】寄包柜
题目描述
超市里有 n ( 1 ≤ n ≤ 1 0 5 ) n(1\le n\le10^5) n(1≤n≤105) 个寄包柜。每个寄包柜格子数量不一,第 i i i 个寄包柜有 a i ( 1 ≤ a i ≤ 1 0 5 ) a_i(1\le a_i\le10^5) ai(1≤ai≤105) 个格子,不过我们并不知道各个 a i a_i ai 的值。对于每个寄包柜,格子编号从 1 开始,一直到 a i a_i ai。现在有 q ( 1 ≤ q ≤ 1 0 5 ) q(1 \le q\le10^5) q(1≤q≤105) 次操作:
1 i j k
:在第 i i i 个柜子的第 j j j 个格子存入物品 k ( 0 ≤ k ≤ 1 0 9 ) k(0\le k\le 10^9) k(0≤k≤109)。当 k = 0 k=0 k=0 时说明清空该格子。2 i j
:查询第 i i i 个柜子的第 j j j 个格子中的物品是什么,保证查询的柜子有存过东西。
已知超市里共计不会超过 1 0 7 10^7 107 个寄包格子, a i a_i ai 是确定然而未知的,但是保证一定不小于该柜子存物品请求的格子编号的最大值。当然也有可能某些寄包柜中一个格子都没有。
输入格式
第一行 2 个整数 n n n 和 q q q,寄包柜个数和询问次数。
接下来 q q q 个整数,表示一次操作。
输出格式
对于查询操作时,输出答案,以换行隔开。
样例 #1
样例输入 #1
5 4
1 3 10000 118014
1 1 1 1
2 3 10000
2 1 1
样例输出 #1
118014
1
思路
定义一个二维映射map<int, map<int, int>> a;,用来存储键值对的映射关系。
输入寄包柜个数n和询问次数q。
循环q次,每次根据输入的操作类型k进行不同的处理:
如果k为1,表示存放操作,继续输入z,并将键值对(x, y)映射到z。
如果k不为1,表示查询操作,直接输出映射的值a[x][y]。
代码
#include<cstdio>
#include<iostream>
#include<map>//引用STL里的map库
using namespace std;
map<int,map<int,int> > a;//建立二维映射
int main(){
int n,q,x,y,k,z;
cin>>n>>q;//输入寄包柜个数及询问次数
for(int i=1;i<=q;++i){//循环q次
scanf("%d%d%d",&k,&x,&y);//先输入k,x,y
if(k==1){//存放操作
scanf("%d",&z);//继续输入
a[x][y]=z;//建立一次映射
}
else{//查询操作
printf("%d\n",a[x][y]);//直接输出所映射的值
}
}
return 0;
}
第五题: [NOIP2004 提高组] 合并果子 / [USACO06NOV] Fence Repair G
题目描述
在一个果园里,多多已经将所有的果子打了下来,而且按果子的不同种类分成了不同的堆。多多决定把所有的果子合成一堆。
每一次合并,多多可以把两堆果子合并到一起,消耗的体力等于两堆果子的重量之和。可以看出,所有的果子经过 n − 1 n-1 n−1 次合并之后, 就只剩下一堆了。多多在合并果子时总共消耗的体力等于每次合并所耗体力之和。
因为还要花大力气把这些果子搬回家,所以多多在合并果子时要尽可能地节省体力。假定每个果子重量都为 1 1 1 ,并且已知果子的种类 数和每种果子的数目,你的任务是设计出合并的次序方案,使多多耗费的体力最少,并输出这个最小的体力耗费值。
例如有 3 3 3 种果子,数目依次为 1 1 1 , 2 2 2 , 9 9 9 。可以先将 1 1 1 、 2 2 2 堆合并,新堆数目为 3 3 3 ,耗费体力为 3 3 3 。接着,将新堆与原先的第三堆合并,又得到新的堆,数目为 12 12 12 ,耗费体力为 12 12 12 。所以多多总共耗费体力 = 3 + 12 = 15 =3+12=15 =3+12=15 。可以证明 15 15 15 为最小的体力耗费值。
输入格式
共两行。
第一行是一个整数
n
(
1
≤
n
≤
10000
)
n(1\leq n\leq 10000)
n(1≤n≤10000) ,表示果子的种类数。
第二行包含 n n n 个整数,用空格分隔,第 i i i 个整数 a i ( 1 ≤ a i ≤ 20000 ) a_i(1\leq a_i\leq 20000) ai(1≤ai≤20000) 是第 i i i 种果子的数目。
输出格式
一个整数,也就是最小的体力耗费值。输入数据保证这个值小于 2 31 2^{31} 231 。
样例 #1
样例输入 #1
3
1 2 9
样例输出 #1
15
题意
多多需要将不同种类的果子合并成一堆,
每次合并两堆果子的体力消耗等于这两堆果子的重量之和。
问题要求设计一个合并次序方案,
使得多多消耗的体力最少,并输出最小的体力消耗值。
思路
根据每种果子的数目,按照从小到大的顺序进行合并。
每次选择合并两堆果子数目最少的堆,使得体力消耗最小。
重复以上步骤,直到所有果子合并成一堆为止。
计算每次合并的体力消耗之和,即为多多总共消耗的体力。
代码
#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main() {
int n;
cin >> n; // 输入果子种类数
priority_queue<int, vector<int>, greater<int>> pq; // 使用优先队列存储果子数目,按照从小到大排序
for (int i = 0; i < n; ++i) {
int num;
cin >> num;
pq.push(num); // 将果子数目放入优先队列
}
int total_cost = 0; // 总体力消耗值
while (pq.size() > 1) {
int a = pq.top(); pq.pop();
int b = pq.top(); pq.pop();
int sum = a + b;
total_cost += sum;
pq.push(sum); // 将合并后的果子数目放回优先队列
}
cout << total_cost << endl; // 输出最小体力消耗值
return 0;
}
总结
使用优先队列(priority_queue)来存储果子的数目,
并按照从小到大的顺序排列。
然后,每次从优先队列中取出最小的两个数目进行合并,
并将合并后的数目放回优先队列中,直到只剩下一个数目为止。
最后输出多多合并果子所需的最小体力消耗值。