Contest3038 - 2021级新生个人训练赛第3场
Contest3038 - 2021级新生个人训练赛第3场
本次训练赛相较于前两次难度上升许多,涉及到了动态规划等算法。为了图方便,会使用部分c++语法,但不影响整体阅读。
问题 A: 冰雹猜想
大意:给定n,如果n是奇数就乘3加一,是偶数就除2,一直循环,直到n变成1为止,并输出n的每个变化。
样例输入
20
样例输出
1 2 4 8 16 5 10 20
基础模拟题,使用while循环处理n,处理后直接输出,以n==1为终止条件。
#include<stdio.h>
int a[5000];
int main(){
int n,l=0;
scanf("%d",&n);
while(n!=1){
a[++l]=n;//记录处理前的数字
if(n%2==1) n=n*3+1;
else n/=2;
}
a[++l]=1;//最后一个是1
for(int i=l;i>=1;i--) printf("%d ",a[i]);
return 0;
}
问题 B: 分数
此题刘老师已经在群里详细解释并给出代码了,如果想要进一步学习快速排序的方法,可以看这里
问题 C: 立方
题目大意:给定一个数字n,算出有多少个数的三次方小于n。
样例输入
100
样例输出
4
提示
111 = 1 222 = 8
333 = 27 444 = 64
555 = 125 但是 125 > 100
本题刘老师提供了一种“投机取巧”的方法,之所以需要投机取巧,根本上是整数类型存储有限,long long类型的上限只有264 左右,远不及题目要求的1028 ,而double类型可以取更大的值(21024),但是double类型是有精度范围的(15位~16位),也就是说在1015 之后,会有精度缺失的问题。严格意义上来讲,如果发生精度缺失的话,是没法解决问题的,但好在数据没有卡double,使用double类型能通过。因此把这种使用double的方法称之为“投机”。
如果想使用严格正确的方法,需要使用高精度这种把数字变成字符来运算的方法,由于此题使用高精度算法费时费力,,不多加赘述。有兴趣的同学点击超链接学习即可。
#include<iostream>
#include<cmath>
using namespace std;
int main() {
double n;
cin >> n;
cout << (int)pow(n,1.0/3);//题目数据没有1e28 偷跑 _(:з」∠)_
}
问题 D: 排队
大意:给定n个不同的数,每次交换两个数,求得到递增序列的最小次数。
每个数都有它本来该去的地方,现在从前到后,把每个数送到它该去的地方。
例如序列4 2 1 5 3
数字4对应第四个位置,把4和5做交换得到序列5 2 1 4 3,以此类推得到3 2 1 4 5 --->1 2 3 4 5
#include<stdio.h>
int a[100005];
int main(){
int n;
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
int ans=0;
for(int i=1;i<=n;i++){
while(a[i]!=i){
int t=a[a[i]];//-------
a[a[i]]=a[i];//交换
a[i]=t;//------
ans++;
}
}
printf("%d\n",ans);
return 0;
}
问题 E: 调研
题目描述
有一直线型展台共有 m 个展位,按该展位离入口处的远近顺序编号,其编号分别为 1、2、……、m;其中只有 n 个是展示新技术的展位,最后一个展示新技术的展位编号为 m。
这次调研分两个小组进行,每个小组最多调研连续的 10 个展位,且每个小组调研的展位至少相隔 2 个展位。
乐乐希望你设计一种安排方案,使领导调研更多的展示新技术的展位
样例输入
5
1 3 12 21 8
样例输出
5
本题使用动态规划算法。
#include <bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int n;
int f[N][3],a[N],s[N],ans;
int mx = -1;
int main () {
cin >> n;
for(int i = 1; i <= n; ++i) {
cin >> a[i];
mx = max(mx,a[i]);//题目没给m,自己找出mx
}
for(int i = 1; i <= n; ++i)
s[a[i]] = 1;
for(int i = 1; i <= mx; ++i)
s[i] += s[i - 1];//记录前缀和
for(int i = 1; i <= mx; ++i) {
//更新第一个小组
for(int k = 0; k <= 10 && i - k >= 0; ++k) //k是第一组的人数
f[i][1] = max(f[i - 1][1],s[i] - s[i - k]);
f[i][2] = f[i - 1][2];
for(int j = 0; j <= 10 && i - j >= 2; ++j)
f[i][2] = max(f[i][2] , f[i-j-2][1]+s[i]-s[i-j]);
}
for(int i = 0; i <= mx; ++i)
ans = max(ans,f[i][2]);
cout << ans << endl;
return 0;
}
问题 F: 求和问题
思路:枚举区间两个端点时间复杂度为O(n^2),一定超时。转换思路根据等差数列求和公式(a1+an)*n / 2 = m , 转换为a1=m / n + 1 / 2 - n / 2 ,枚举n的值,判断a1是否为整数,时间复杂度为O(n),依旧超时,需要继续优化。我们从1开始枚举,需要确定上界,设a1=1,变换得\(\sqrt{2}\)得到n的最大值
#include<cstdio>
#include<cmath>
int main() {
long long n,m;
scanf("%lld%lld",&n,&m);
for(long long i=(long long)sqrt(2*m+1);i>0;i--) {//公式近似化简一下,sqrt为开方函数
double a1=m*1.0/i - i*1.0/2 + 1.0/2;//计算a1的值
if(a1==(long long)a1&&a1>0){//判断是否为正整数
long long x=(long long)a1;
if(x+i-1>n) continue;
printf("[%lld,%lld]\n",x,x+i-1);
}
}
}
问题 G: 吃水果问题
#include<bits/stdc++.h>
using namespace std;
int ct[10010];//记录不同数字个数
int main(){
int t,mx,mxa,a,sum,n;//mx用来记录最大个数,mxa记录最大个数的数字,sum对其他数字个数求和
scanf("%d",&t);
while(t--){
memset(ct,0,sizeof(ct));//有多组测试样例,每次清空一下;
mx=sum=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a);
ct[a]++;//每输入一个数字,该数字个数加1;
if(ct[a]>mx) mx=ct[a],mxa=a;//更新mx和mxa
}
for(int i=1;i<=n;i++) if(i!=mxa) sum+=ct[i];//对其他数字个数求和;
if(mx-sum>1) printf("N\n"); //最大个数假如比其他数字的个数多2个以上就不能成立
else printf("Y\n");
}
return 0;
}

浙公网安备 33010602011771号