可达鸭 CSP-J复赛模拟2补题报告

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.赛后总结

本次比赛中,对题目理解不是很透彻,数据范围也没有注意,以后要多多审题,更加仔细。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值