CSP-J复赛模拟补题报告
2023.10.3星期two
目录
1.比赛分数
共四题,满分400,比赛种拿到70分
T1(transfer):70
T2(calc):0
T3(transit):0
T4(exclusive):0
2.比赛过程
T1:太过自信了,没深读题直接做的,导致后面代码的表达直接出错。
T2:想过很多种优化方法(比如从小输入输入直接结束之类的还有和同近积大)但要么就是起不到优化作用反而更麻烦要么就是可行度为0。最后时间不够了匆忙暴力结果freopen还写错文件名了,直接爆0.
T3:做这道题的时候昨晚想的时间规划已经全乱了,还有最后一个小时我就看了遍题匆匆做的,一开始的思路以为是桶标记所有区间里的点,然后选标记次数最多的点当X,前2个样例过了但我看到最后一个样例就知道这种方法不可行了单反早看到亿点最后也没时间改了就当骗分去做了,结果我数组开打主函数里直接爆空间了。
T4:一开始以为是考位运算,后来越看越不对劲,直接骗分了(没时间还想去想想第二题)
3.题解报告
1.人员借调transfer
题意:小可从A地出发去B地做n件事情,没件事情的用时为An。如果干一件事超过240,回来就要被监禁10080.问最少多长时间。(干每件事情里不能停下)
题目解析:分为3种情况
(1)有An超过240:从B全部干完破罐子破摔回A监禁,时间为sum+400+10080.
(2)没有An超过240但是一次性干完活回A监禁比B短,时间为sumn+400+10080.
(3)没有An超过240但是需要反复跑刷新B地待的时间。
AC码:
#include<iostream>
using namespace std;
long long a[1010];
int main(){
long long n,cnt=0,t=400,f=1,t2=400;
cin>>n;
for(long long i=1;i<=n;i++){
cin>>a[i];
if(a[i]>=240){
f=0;
}
t+=a[i];
}
if(t>=640){
t+=10080;
}
if(f==0){
cout<<t;
return 0;
}
else{
cnt=a[1];
for(long long i=2;i<=n;i++){
if(cnt+a[i]>=240){
t2+=400+cnt;
cnt=a[i];
}
else{
cnt+=a[i];
}
}
t2+=cnt;
}
if(f==1&&t2==400&&cnt!=0){
t2=cnt;
}
if(f==1&&t2>=t){
cout<<t;
}
if(f==1&&t2<t){
cout<<t2;
}
return 0;
}
2.计算calc
题意:有m,n,k三个数字,求出n~m之间各数位上相加和=k的数,取其中乘积最大的一个数;
题目解析:题出的很直白但是没有优化思路只能拿到70分。(当然,freopen写错会爆0)
优化主要是用打表打到大On。
AC码:
#include<bits/stdc++.h>
using namespace std;
int n,p[5000005],e[5000005];
int main()
{
p[0]=1;
for(int i=1;i<=5000000;i++){
p[i]=p[i/10]*(i%10);
e[i]=e[i/10]+(i%10);
}
p[0]=-1;
int t;
cin>>t;
for(int i=1;i<=t;i++){
int m,k,ans=0;
cin>>n>>m>>k;
for(int i=n;i<=m;i++){
if(e[i]==k&&p[i]>p[ans]){
ans=i;
}
}
cout<<ans<<" "<<p[ans]<<endl;
}
return 0;
}
3.智能公交transit
题意:马路上总共有 n 个公交站台,有一辆智能公交车会在这n个站台之间穿梭。求一个点x可以让公交车每次送完人后停靠在x,后再搭乘人最省路程。
题目解析:一个动规题不过贪心正好是10的7次方不会爆,把区间看作长度的两个点找到中间值直接求长度就完了。
AC码:
#include<bits/stdc++.h>
using namespace std;
int a[1000001];
int main(){
int n,m,x,y,cnt=0,sum=0;
cin>>n>>m;
for(int i=1;i<=m;i++){
cin>>x>>y;
sum+=abs(x-y);
a[++cnt]=x;
a[++cnt]=y;
}
sort(a+1,a+cnt+1);
int pos=cnt/2;
for(int i=1;i<=cnt;i++){
sum+=abs(a[pos]-a[i]);
}
cout<<a[pos]<<" "<<sum;
return 0;
}
4.异或和exclusive
题意:多个集合中总共有 n 个数字,并且已知每个数字的大小 ai和属于某个集合 bi。在一个集合中选择一个数字,收益为这个数字的大小,选择多个数字,收益为这些数字的异或和。总收益为每个集合的收益之和。而且要求最大值。
题目解析:BT题,动规背包+位运算(没听懂直接copy了)
#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.比赛总结
犯的错误太多了不管是freopen亦或者数组大小。下次多多注意吧。。。