CSP-J复赛模拟2王皓轩补题报告
日期:2023年10月2日星期一
1.比赛概况
比赛总共4题,满分400,赛时拿到130分,其中第一题10分,第二题70分,第三题50分,第四题0分。
2.比赛过程
上来就把第一题做了(最后的时候发现bug,改了后提交时没有保存),然后第二题直接枚举暴力,过了70分,做三题的时候直接用每个点算,过50分,第四题有dp思路,但循环和转移方程等都错了。
3.题解报告
(1)人员借调
情况:赛中10分,已补题
题意:有一个A,B两地领导都很喜欢的一个员工,b地的领导让他来帮忙,而如果离开a地大于等于2400分钟,a地的领导会把他放在a地7天(10080min),问需要多久处理完a地的事情。
赛中想法:想算加起来超过240分钟就先回去,或有240分钟也回去。
题解:开始累加就要有一个来回(没保存上丢了50分),然后看总时间小于240,呆在B全处理完再返回,则只需计算1次往返。如果大于240,就看是直接摆烂不回a地全部做完好(花费=总时间+滞留+往返400),还是多次回a地避罚好。
AC代码:
#include<bits/stdc++.h>
using namespace std;
int sum,cnt,n,x,a[1005],ans=400;
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(cnt*400+ans,ans+10080);
}
cout<<ans;
return 0;
}
(2)计算
情况:赛中70分,已补题
题意:有m,n,k三个数字,求出n~m之间各数位上相加和=k的数,取其中乘积最大的一个数;
赛中想法:觉得时间限制不至于超,直接暴力枚举n~m之间的每一个数,再数位分离求。
题解:用空间换时间,提前处理,用O(n)的复杂度存好1~5000000的每个数个数位上的乘积和和。
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;
while(t--){
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)智能公交
情况:赛中50分,已补题
题意:马路上总共有 n 个公交站台,有一辆智能公交车会在这n个站台之间穿梭。求一个点x可以让公交车每次送完人后停靠在x,后再搭乘人最省路程
(输入的a,b代表车要从x到a,再从a到b,最后从b回到x。)
赛中想法:知道要贪心,但是没想到怎么贪(doge),但是在每个车站上暴力模拟,最后得了50分。
题解:后面的样例中说道其实是一个区间,在区间求中可以先想若x点在a,b点外面,那么x到a或b会多两倍的距离,可以做一个差分+前缀和,求其本就要走的2倍a到b距离,最后加上额外的路程。
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;
while(t--){
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;
}
(4)异或和
情况:赛中0分,已补题
题意:多个集合中总共有 n 个数字,并且已知每个数字的大小 ai和属于某个集合 bi。在一个集合中选择一个数字,收益为这个数字的大小,选择多个数字,收益为这些数字的异或和。总收益为每个集合的收益之和。而且要求最大值。
赛中想法:看出来了这是一道dp的分组背包,但是想求一共有多少组,然后直接套上,不过转移方程也不对。
题解:由于数据范围过大,没有办法开三维。用二维
数组,用完后直接清空,再多用一个二维的num数组存异或值。存好后进行分组背包,时间复杂度为O(n2)(n的平方)。
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.赛后总结
本次比赛中,对题目理解不是很透彻,数据范围也没有注意,以后要多多审题,更加仔细。