CSP-J第二场模拟赛补题报告2024.10.2

似乎有人会看我的博客?点个赞再走呗

一、题目报告

第一题AC,第二题20pts,第三题AC,第四题45pts。比赛后AK。

二、比赛概况

比赛开始10min没咋费劲AC了T1,觉得T4好些,开干T4,50min,写出了T4的暴力只拿了45分。1h10minAC了T3(难蚌),1h写了T2爆搜。5min摆烂的考生是屑

三、解题报告

T1 下棋(chess)

得分情况

比赛时AC。

题目大意

有 n 个人,每个人有1、2、3星怪兽。3个1星怪合成2星,3个2星怪合成3星。给定战力计算公式,将玩家按战力大小排序,若战力相同,按序号大小比较。

解题思路

有两个排序规则,可以想到结构体排序。以战力作为第一关键字,序号大小作为第二关键字。

正解程序 

#include<bits/stdc++.h>
using namespace std;
struct node{
	long long s1,s2,s3,sco,num;
}a[100005];
bool cmp(node x,node y){
	if(x.sco!=y.sco)return x.sco>y.sco;//结构体排序
	else return x.num<y.num;
}
int main(){
//  freopen("chess.in","r",stdin);
//	freopen("chess.out","w",stdout);
	int n;
    cin>>n;
    for(int i=1;i<=n;i++){
    	cin>>a[i].s1>>a[i].s2>>a[i].s3;
    	a[i].num=i;
    	if(a[i].s1>=3){
    		a[i].s2+=a[i].s1/3;//合成2星
    		a[i].s1%=3;
		}
		if(a[i].s2>=3){
    		a[i].s3+=a[i].s2/3;//合成3星
    		a[i].s2%=3;
		}
		a[i].sco=a[i].s1+a[i].s2*3+a[i].s3*18;//计算战力
	}
	sort(a+1,a+1+n,cmp);
	for(int i=1;i<=n;i++){
		cout<<a[i].num<<' ';
	}
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

T2 汪洋(BigWater)

得分情况

比赛时20pts。

题目大意

一个 n * n 大小的地图,其中每个点有其权值,走过即获得权值。要求在地图内走一圈,每次只能右转90^{\circ} ,且不能走重复的路,回到原点且使获得权值最大。第一步必须向右走。

 解题思路

爆搜时间复杂度太高,20pts(甚至不会剪枝)

每次只能右转90^{\circ},且不走回头路,可以得出走过的路径是一个矩形。利用前缀和计算每条边的权值,求最大周长即可。

正解代码

#include<bits/stdc++.h>
using namespace std;
const int N=1005;
int a[N][N], row[N][N], col[N][N];
int main() {
	int n;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		for (int j = 1; j <= n; j++) {
			cin >> a[i][j];
			row[i][j] = row[i][j - 1] + a[i][j];//前缀和计算边长
			col[i][j] = col[i - 1][j] + a[i][j];
		}
	}
	int ans = 0;
	for (int x = 1; x <= n; x++)for (int y = 1; y <= n; y++)//枚举周长
			ans = max(ans, row[1][y] + row[x][y] + col[x][1] + col[x][y] - a[1]
			          [1] - a[1][y] - a[x][1] - a[x][y]);
	cout << ans + 100 << endl;
	return 0;
}

 T3 删数(delnum)

得分情况

比赛时AC。首A。

题目大意

有一个集合内有 1 到 114514^{1919810}(首 的所有正整数。给定a数组,集合内a_{1},a_{2}……a_{n}大的数会被同时删除。给定多组数据,每组一个整数 x ,问 x 会在第几轮被删除。

解题思路

当a数组最大的数还是小于x,证明x一定不会在下一轮被删除。能算不举,利用除法和取模运算算出x到a中最大数还需要删几次,判断是否能取模。能则输出,否则跳过这个数继续算。被跳过的数不再删除。

正解代码

#include<bits/stdc++.h>
using namespace std;
long long n,a[100005],q,x,m,num;
int main() {
//	freopen("delnum.in","r",stdin);
//	freopen("delnum.out","w",stdout);
	cin>>n;
	m=n;
	for(int i=1; i<=n; i++) {
		cin>>a[i];
	}
	cin>>q;
	while(q--) {
		cin>>x;
		num=0,m=n;
		int sum=0;
		while(m) {
			if(x>=a[m])
				x-=a[m];//防止删过头
			else {
				m--;
				continue;
			}
			if(1.0*x/m<1)sum=1;
			else
				sum=ceil(1.0*x/m);
			num+=sum;
			if(x%m==0) {
				if(x==0)//若x==a[i],就不用再删一次
					cout<<num<<endl;
				else cout<<num+1<<endl;
				num=-114514;//臭死力
				break;
			} else {
				x+=a[m];
				x-=sum*m;//跳过下一个数
				m--;
			}
		}
		if(num!=-114514) {
			cout<<0<<endl;
		}
	}
//	fclose(stdin);
//	fclose(stdout);
	return 0;
}

T4 平分糖果(candy)

得分情况

比赛时45pts。

题目大意

有甜度为1-6的糖果若干,给出每种甜度糖的数量,问是否可以在不分割糖的情况下把糖分成两堆,使每一堆甜度相等。

解题思路

每个物品的数量有限,所以找出是否存在几个物品的价值与物品总价值的一半相同就可以了,可以抽象成多重背包。
把糖果的总价值除以2,就是需要达到的目标和。接下来就是多重背包求可行性问题:用 dp[i][j] 表示前 i 种物品,能否凑出价值 j 来。
对于第 i 种物品,都可以取 [0,a[i]] 个,假设当前取了k个,则其状态转移为:
dp[i][j] |= dp[i-1][j-k*i]

正解代码

#include<bits/stdc++.h>
using namespace std;
int a[10], dp[10][40000], col = 0;
int main() {
	freopen("candy.in", "r", stdin);
	freopen("candy.out", "w", stdout);
	while (1) {
		int m = 0;
		for (int i = 1; i <= 6; i++) {
			cin >> a[i];
			m += a[i] * i;
		}
		if (!m)
			break;
		dp[0][0] = 1;
		for (int i = 1; i <= 6; i++)for (int k = 0; k <= a[i]; k++)for (int j = k * i; j <= m / 2 + 1; j++)
					dp[i][j] |= dp[i - 1][j - k * i];
		cout << "Collection #" << ++col << ':' << endl;
		if (m % 2 == 0 && dp[6][m / 2] == 1)
			cout << "Can be divided." << endl;
		else
			cout << "Can't be divided." << endl;
		cout << endl;
	}
	return 0;
}

 四、总结

比上次考的好一点,希望再增强题目分析能力,夯实算法基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值