第十一周作业
目录
- MT2012 竹鼠的白色季节
- MT2013 照亮街道
- MT2014 打靶
- MT2026 二维坐标点移动
- MT2027 一秒成零
MT2012 竹鼠的白色季节
难度:黄金 时间限制:1秒 占用内存:250M
题目描述
小码哥的竹鼠养殖场中的竹鼠都在一条直线的隔间里,一共有 n ( 1 ≤ n ≤ 100000 ) n(1≤n≤100000) n(1≤n≤100000) 只竹鼠,它们的坐标分别为 p 1 , p 2 , p 3 , … , p n ( 0 ≤ p i ≤ 1 0 8 ) p_1, p_2,p_3,…,p_n (0≤p_i≤10^8 ) p1,p2,p3,…,pn(0≤pi≤108)。
又到了冬季,竹鼠们也要谈恋爱,因为竹鼠们都太胖了,它们的活动范围有限,这里统一规定它们的活动范围为 d ( 0 ≤ d ≤ 1 0 4 ) d(0≤d≤10^4 ) d(0≤d≤104)。
由于竹鼠之间也需要双向奔赴,所以如果两只竹鼠之间的距离小于等于 d d d,则称它们是有缘的一对竹鼠,问小码哥的养殖场里一共有多少对有缘的竹鼠?
注:一只竹鼠可能存在于多个有缘的竹鼠对之中,多只竹鼠可能在同一个坐标上。格式
输入格式:第一行为两个用空格隔开的数字 n n n 和 d d d;;
第二行为 n n n 个整数,表示 n n n 只竹鼠在直线上的坐标。
输出格式:输出一个整数,表示有缘的竹鼠对 。样例 1
输入:5 3
1 2 4 8 9
输出:4
相关知识点:
枚举
题解
题目说了很多,但实际只需要知道一件事即可:“如果两只竹鼠之间的距离小于等于 d d d,则称它们是一对有缘的竹鼠,问小码哥的养殖场里一共有多少对有缘的竹鼠?”。所以,本题实际上就是让我们统计位置差距超过一定范围的数对。
具体的求解思路很简单,将所有竹鼠的位置进行排序,然后再扫描排序后的数组并统计满足要求的差距即可,但是有一点需要注意,在统计差距时为了避免重复(因为对于一对有缘的竹鼠而言,两个竹鼠各自在计算差距时都会将彼此纳入计算),我们只进行 “单向” 统计,具体实现如下(已 AC):
/*
MT2012 竹鼠的白色季节
*/
#include<bits/stdc++.h>
using namespace std;
const int MAX = 100005;
int ary[MAX];
int getPredestined(int ary[], int n, int d)
{
int predestined = 0;
// 对竹鼠的位置数组进行排序
sort(ary,ary+n);
// 扫描全部竹鼠以寻找有缘鼠
// 注:为避免重复统计,规定进行单向寻找(下面的代码执行后向配对)
for(int i=0;i<n-1;i++)
for(int j=i+1;j<n;j++)
if(ary[j]-ary[i] <= d) predestined++;
else break;
return predestined;
}
int main( )
{
// 录入数据
int n, d; cin>>n>>d;
for(int i=0;i<n;i++) cin>>ary[i];
// 输出有缘的竹鼠对
cout<<getPredestined(ary, n, d)<<endl;
return 0;
}
MT2013 照亮街道
难度:黄金 时间限制:1秒 占用内存:128M
题目描述
有一条可以视为线段的街道,长度为 L L L,其中有 n n n 个路灯,每个路灯能照亮的半径都是相同的,求最小的照亮半径 r r r 使得整条街道上的每一个点都能被照亮。
格式
输入格式:第一行两个正整数 n , L n,L n,L;
第二行为 n n n 个实数,表示 n n n 个路灯的坐标,数据保证在 0 0 0 到 L L L 之间。
输出格式:输出一行一个实数表示答案,答案保留两位小数。样例1
输入:7 15
15 5 3 7 9 14 0输出:2.50
备注
其中: n ≤ 1000 , L ≤ 1 0 9 n≤1000,L≤10^9 n≤1000,L≤109
相关知识点:
枚举
题解
求解此题最简单的办法就是暴力枚举。由于题目让我们保留两个小数,因此求解步骤可以从 0.01 开始进行枚举,直到某个半径长度能够将整条街都照亮时,该半径即为所求。
但是从数学的角度出发,肯定有更好的算法。
对于这条路灯间距不一致的街而言,如果要让所有地方都充满光明,则路灯的照明半径肯定只取决于那段“路灯隔得最远的路段”。比如,在题目给出的数据中,从下图可以很明显地看出,其只取决于路灯 9 和 14 的距离。
因此,我们的求解目标就从“找最小半径”,转换为“找最远路灯”。最远路灯的求解就很简单了:对路灯的位置数组进行排序,然后遍历该数组求差分的最大值即可。但是!一定要注意两个端点位置(即绝对位置为 0 和 L 处)的与路灯的差距,这需要单独计算!并参与对最大值的比较。
下面给出基于以上思路写出的完整代码(已 AC):
/*
MT2013 照亮街道
*/
#include<bits/stdc++.h>
using namespace std;
const int MAX = 1005;
// 注:用 float 只能通过一个例子,这里必须用 double
double ary[MAX];
int main( )
{
// 录入数据
int n, L; cin>>n>>L;
for(int i=0;i<n;i++) cin>>ary[i];
// 对路灯数组排序
sort(ary,ary+n);
// 求差距最大的间隙
double gap=0;
for(int i=0;i<n-1;i++)
gap = max(gap,ary[i+1]-ary[i]);
// 与路段的首尾间隙进行比较
gap = max(gap/2, max(ary[0],L-ary[n-1]));
// 输出操作次数(注意格式控制)
printf("%.2f\n", gap);
return 0;
}
MT2014 打靶
难度:黄金 时间限制:3秒 占用内存:128M
题目描述
小码哥在练习射箭,他要射穿 n n n 个靶子,且每个靶子有高度 h i h_i hi。箭会因为受到靶子的摩擦力而下坠,当射穿一个靶子后箭的高度会下降 1。小码哥可以射任意高度的箭。
求出小码哥最少需射几箭。格式
输入格式:第一行一个整数 n n n;
第二行 n n n 个整数 h i h_i hi。
输出格式:输出一个整数,表示小码哥最少需要射几箭。样例 1
输入:5
2 1 5 4 3
输出:2备注
其中: 1 ≤ n , h i ≤ 1 0 6 1≤n,h_i≤10^6 1≤n,hi≤106。
小码哥与靶子的占位如下(不会有两个相邻的靶子紧紧贴在一起):
小码哥 ——> 靶子1,靶子2,靶子3,……,靶子n。
相关知识点:
模拟
、枚举
题解
题目的意思很简单:射出的箭具有无限射程,但是每当该箭射穿一个靶子时就会降低1个单位的高度。现在有一系列的靶,问最少要射几支箭才能把这些靶都击穿。
题目给出的测试数据如下:
显然,两支箭就能将所有靶全部击倒(顺序可任意):
取第一支箭高度为2,它在射穿0号靶子后,会顺势射穿1号靶;
取第二支箭高度为5,它在射穿2号靶子后,会顺势射穿3、4号靶;
这样就能用最少的箭将全部靶子都射穿。
接下来我们来思考这里面的奥义。首先要知道一件事,箭的射程是无限的,对于射出的任意箭,它必定能将一系列高度为等差递减序列的靶子全部射穿。这实际上提示了我们,该题就是让我们其统计高度连续递减靶子的丛数。但要注意一件事,这个等差递减序列的先后关系必须和各靶子的相对位置对应。因此,我们在模拟这个射箭的过程时,要考虑到这一点。所以现在问题的关键就在于,如何去记录高度连续递减靶子的丛数?要知道,对于题目输入的靶子而言,那些高度连续递减靶子位置可能本身并不连续。
这里提供一种简单有效的办法:高度消融。具体做法是,对于输入的高度,我们每次都将当前高度进行下沉,这样最终就能把不同位置的具有连续递减性质的丛数给记录下来。例如,对于高度序列:{5,3,4,3,1},系统第一次运行时,高度数组 height[ ]={0}为空:
- 输入高度 5,由于这之前并没有这个高度(即 height[5]==0),说明当前这个高度是第一个,因此需要记 cnt=1,然后将其下沉,即现在高度数组的内容为:height[5-1]=height[4]++,即 height={0,0,0,0,1,0};
- 输入高度 3,由于这之前并没有这个高度(即 height[3]==0),说明当前这个高度是第一个,因此需要记 cnt=2,然后将其下沉,即现在高度数组的内容为:height[3-1]=height[2]++,即 height={0,0,1,0,1,0};
- 输入高度 4,由于当前高度数组存在 height[4] 非0,说明当前高度存在着从上一次消融下来的数据,也就是说在这之前射出了一支高度为 4+1=5 的箭,那么当前位置将被射穿。因此,要将当前位置进行消融并下沉,于是置 height[4]–,height[4-1]=height[3]++,即 height={0,0,1,1,0,0};
- 输入高度 3,由于当前高度数组存在 height[3] 非0,说明当前高度存在着从上一次消融下来的数据,也就是说在这之前射出了一支高度为 3+1=4 的箭,那么当前位置将被射穿。因此,要将当前位置进行消融并下沉,于是置 height[3]–,height[3-1]=height[2]++,即 height={0,0,2,0,0,0};
- 输入高度 1,由于这之前并没有这个高度(即 height[1]==0),说明当前这个高度是第一个,因此需要记 cnt=3,然后将其下沉,即现在高度数组的内容为:height[1-1]=height[0]++,即 height={1,0,1,0,0,0}。
算法结束,输出计数cnt即得到了最终需要的最少的箭数。
根据这样的思路可写出求解本题的完整代码(已 AC):
/*
MT2014 打靶
*/
#include<bits/stdc++.h>
using namespace std;
const int MAX = 1000005;
int ary[MAX];
int main( )
{
int n, height, cnt=0; cin>>n;
while(n--){
// 获取当前靶子的高度
cin>>height;
// 根据当前靶子高度是否存在决定是否消融和计数
ary[height]?ary[height]--:cnt++;
// 执行下沉
ary[height-1]++;
}
cout<<cnt<<endl;
return 0;
}
MT2026 二维坐标点移动
难度:黄金 时间限制:1秒 占用内存:128M
题目描述
给定一个 N ∗ M N*M N∗M 的二维坐标矩阵A,并且给定一个整数K,K表示移动步数,矩阵A中的所有点 A [ i ] [ j ] A[i][j] A[i][j] 的移动操作如下:
- A [ i ] [ j ] A[i][j] A[i][j] 移动到 A [ i ] [ j + 1 ] ( j ≤ M − 2 ) A[i][j+1](j ≤ M-2) A[i][j+1](j≤M−2);
- A [ i ] [ M − 1 ] A[i][M-1] A[i][M−1] 移动到 A [ i + 1 ] [ 0 ] ( i ≤ N − 2 ) A[i+1][0](i ≤ N-2) A[i+1][0](i≤N−2);
- A [ N − 1 ] [ M − 1 ] A[N-1][M-1] A[N−1][M−1] 移动到 A [ 0 ] [ 0 ] A[0][0] A[0][0]。
最后输出移动K次后新的二维坐标矩阵A。
格式
输入格式:第一行输入两个整数 N, M;
接下来 N 行,每行 M 个数,即 A [ i ] [ j ] ( 0 ≤ i ≤ N , 0 ≤ j ≤ M ) A[i][j](0 ≤ i ≤ N, 0 ≤ j ≤ M) A[i][j](0≤i≤N,0≤j≤M);
最后一行一个整数 K。
输出格式:N 行,每行 M 个数,即修改之后的 A [ i ] [ j ] A[i][j] A[i][j]。样例 1
输入:3 3
1 2 3
30 40 50
10 20 30
2输出:20 30 1
2 3 30
40 50 10备注
其中: 1 ≤ N , M ≤ 50 , − 1000 ≤ A [ i ] [ j ] ≤ 1000 , 0 ≤ K ≤ 1000 1≤N,M≤50,-1000 ≤ A[i][j]≤ 1000,0 ≤ K ≤ 1000 1≤N,M≤50,−1000≤A[i][j]≤1000,0≤K≤1000。
相关知识点:
模拟
题解
实际上,这道题的要求是“将矩阵中的每个元素往后移动K个单位”。由于处理对象是一个矩阵,因此在将其中的元素向后移动时有3个情况(假设矩阵规格为 N ∗ M N*M N∗M,索引从0开始):
- 待移动元素位于矩阵最右下角的位置(即 A [ N − 1 ] [ M − 1 ] A[N-1][M-1] A[N−1][M−1]),这种情况,其下一个位置为矩阵的最开始位置(即 A [ 0 ] [ 0 ] A[0][0] A[0][0]);
- 待移动元素位于矩阵任意行(除最后一行)的末尾(即 A [ i ] [ M − 1 ] A[i][M-1] A[i][M−1]),这种情况,其下一个位置为该元素所在下一行的最开始位置(即 A [ i + 1 ] [ 0 ] A[i+1][0] A[i+1][0]);
- 待移动元素位于除1和2以外的任意位置(设为 A [ i ] [ j ] A[i][j] A[i][j]),这种情况,其下一个位置为 A [ i ] [ j + 1 ] A[i][j+1] A[i][j+1]。
从上面的分析不难看出,其变换规则还是很简单的。因此,最简单直接的办法就是设计一个规格和输入数据一致的二维数组,然后遍历原始数据,通过 switch 控制不同类型的数据进行位置变换并存储即可。这样的方式显然是可行的。
但仔细思考上面的三个情况,不难发现其本质都是在描述“将数据向后移动若干位”的含义。因此,为了简化上述复杂的分情况讨论环节,我们可以用一种更简单的方式进行:将原始数据存储至一维空间,执行“将数据向后移动若干位”后再按二维的方式进行输出。
根据这样的思路最终可得到以下代码(已 AC):
/*
MT2026 二维坐标点移动
*/
#include<bits/stdc++.h>
using namespace std;
const int MAX = 2505;
int ary[MAX], offset[MAX];
int main()
{
// 录入数据
int n, m, k; cin>>n>>m;
int LEN = n*m;
for(int i=0;i<LEN;i++) cin>>ary[i];
cin>>k;
// 对数组进行移位处理
for(int i=0;i<LEN;i++) offset[(i+k)%LEN] = ary[i];
// 输出数据
k = 0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++)
cout<<offset[k++]<<" ";
cout<<endl;
}
return 0;
}
MT2027 一秒成零
难度:白银 时间限制:1秒 占用内存:128M
题目描述
给定一个正整数 n ,请写一个函数 Steps,将 n 通过以下操作变成0,并且返回操作次数。
- n 为偶数,则 n=n/2;
- n 为奇数,则 n=n-1。
格式
输入格式:第一行输入正整数 n n n;
输出格式:输出操作次数。样例1
输入:14
输出:6
备注
其中: 1 ≤ n ≤ 1 0 7 1≤n≤10^7 1≤n≤107。
相关知识点:
模拟
题解
这是一道纯模拟题,照题目意思做就行。要么递推要么递推(取决于鉴于数据范围),下面给出求解的两个算法:
/*
MT2027 一秒成零
*/
#include<bits/stdc++.h>
using namespace std;
// 递推实现(数的取值范围较大时宜用)
int Steps(int n){
int optCnt = 0;
while(n){
if(n&1) n--;
else n/=2;
optCnt++;
}
return optCnt;
}
// 递归实现
int Steps_(int n){
if(n==0) return 0;
if(n&1) return Steps(n-1)+1;
return Steps(n/2)+1;
}
int main( )
{
// 获取输入
int n; cin>>n;
// 输出操作次数
cout<<Steps(n);
return 0;
}