日期:2023年10月2日星期一
学号:S14029
姓名:黄子航
1. 比赛概况:
比赛总分共 4 题,满分 400,赛时拿到60 分,其中第一题 30 分,第二题 0 分,第三题 20 分,第
四题 10 分。
2. 比赛过程:
比赛时按照顺序做的,第一题一开始是暴力求解,优化也做得不错,但是漏了一种可能,所以分很低,
3. 题解报告:
(1) 第一题:人员借调
情况:赛中30分,已补题。
题意:一人在A地工作中非常出色,B地领导想让他帮忙处理一些工作,但由于此人工作出众,如果他连
续呆在B地大于等于240分钟时,回来后就会强制留在A地7天(10080分钟),来回AB地也需要400分钟,问
此人最少多少分钟完成所有工作项目?
赛时本题做题想法:暴力解题,但没有想到可以把所有任务都做完在回去。
题解:分情况讨论+优化
AC 代码:
#include<bits/stdc++.h>
using namespace std;
int a[1005],n,x,ans=400,sum,cnt;
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>x;
sum+=x;
ans+=x;
if(sum>=240){
cnt++;
sum=x;
}
}
if(cnt>=1){
if(n==1)
ans+=10080;
else
ans=min(ans+10080,ans+cnt*400);
}
cout<<ans;
return 0;
}
(2) 第二题:计算
情况:赛中0分,已补题。
题意:使用n,m,k算出x的数值,1、n<=x<=m。2、x十进制各位上的和等于k。如果x有多个,输出
各位数乘积最大的一个,如果乘积也相等,则输出x最小的一个。
赛时本题做题想法:思路有很多,但是都编不出来,只好输出了题例。
题解:简简单单的枚举法,然后优化。
AC 代码:
#include<bits/stdc++.h>
using namespace std;
int e[5000005],p[5000005];
int main(){
//freopen("calc.in","r",stdin);
//freopen("calc.out","w",stdout);
p[0]=1;
for(int i=1;i<=5000000;i++){
e[i]=e[i/10]+(i%10);
p[i]=p[i/10]*(i%10);
}
p[0]=-1;
int t;
cin>>t;
while(t--){
int m,n,k,ans=0;
cin>>m>>n>>k;
for(int i= m;i<= n;i++){
if (e[i]==k&&p[i]>p[ans]){
ans=i;
}
}
cout<<ans<<" "<<p[ans];
}
fclose(stdin);
fclose(stdout);
return 0;
}
(3) 第三题:智能公交
情况:赛中20分,已补题。
题意:马路上有n个公交车站,编号从1~n,每两个站都相隔1千米,有一辆智能公交车,
无人时停在x站,有人呼叫公交车时,公交车会很快开到那一站,接乘客上车,到达后让
乘客下车,再返回x站。请问x站在哪里时,公交车跑的路程最少?若最少的有多个,输出
编号靠前的站台。
赛时本题做题想法:暴力枚举法,计算每个车站作为x站时需要开的路程,再进行比较,
输出路程最短的。
题解:本题应该使用贪心算法,通过计算可以发现,x站在起点终点之间时,所需的路
程为2|起点-终点|,在起点终点之外时,所需的路程就是2|离x较远的点-x|,后者肯定是
大于前者的。由此可知,本题中贪心算法表现为尽量多的占据起点和终点之间。
AC 代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m,a,b;
long long ans=1e18,pos,sum=0;
cin>>n>>m;
vector<long long> p(n+2),s(n+2),p1(n+2),s1(n+2);
while(m--){
cin>>a>>b;
p[a-1]+=2;
s[b+1]+=2;
sum+=(b-a)*2;
}
for(int i=n;i>=1;i--){
p[i]+=p[i+1];
p1[i]=p1[i+1]+p[i];
}
for(int i=1;i<=n;i++){
s[i]+=s[i-1];
s1[i]+=s1[i-1]+s[i];
if(s1[i]+p1[i]<ans){
pos=i;
ans=s1[i]+p1[i];
}
}
cout<<pos<<" "<<ans+sum<<endl;
return 0;
}
(4) 第四题:异或和
情况:赛中10分,已补题。
题意:多个集合中有n个数字,
在一个集合中选择一个数字,收益就是这个数字,选择多个
数字,收益是这些数字的异或和,最多选取m个数字,问每个集合的收益和最大位多少。
赛时本题做题想法:暴力枚举法,并且只写了一半,感觉没法写,直接开摆。
题解:使用dp算法,通过二维数组优化来解决空间问题,最后在进行枚举选出最大。
AC 代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,dp[2005][2050],num[2005][2005],dpp[2050],zz[2005];
vector<int> ve[2005];
int main(){
cin>>n>>m;
for (int i=1;i<=n;i++){
int x,y;
cin>>x>>y;
ve[y].push_back(x);
zz[y]++;
}
for(int i=1;i<=n;i++){
for(int j=1;j<=2047;j++){
dp[i][j]=1e9;
}
}
for(int zu=1;zu<=2000;zu++){
if (zz[zu]!=0)
dp[1][ve[zu][0]]=1;
for(int i=2;i<=zz[zu];i++){
dp[i][ve[zu][i-1]]=1;
for(int j=1;j<=2047;j++){
if(dp[i-1][j]!=1e9){
dp[i][j]=min(dp[i][j],dp[i-1][j]);
dp[i][j^ve[zu][i-1]]=min(dp[i-1][j]+1,dp[i][j^ve[zu][i-1]]);
}
}
}
for(int j=1;j<=2047;j++){
if(dp[zz[zu]][j]!=1e9)
num[zu][dp[zz[zu]][j]]=j;
}
for(int i=1;i<=zz[zu];i++){
for(int j=1;j<=2047;j++){
dp[i][j]=1e9;
}
}
}
for(int i=1;i<=2000;i++){
for(int j=m;j>= 1;j--){
for(int k=1;k<=zz[i];k++){
if(j>=k)
dpp[j]=max(dpp[j],dpp[j-k]+num[i][k]);
}
}
}
cout<<dpp[m];
return 0;
}
4. 赛后总结:
本次模拟赛由于知识不够充分,编程能力较弱的原因导致分数低,我应该多做练习,了解新
的知识,加强编程能力,从而使下次避免出现此类的错误。