PAT_520钻石争霸赛_2020(103)

本文详细介绍了两道编程题目,一是关于计算阶乘并去除末尾0后获取指定长度数字的解决方案,强调了精度和效率的问题;二是利用二分查找法解决组合计数问题,讨论了暴力解法与优化策略。此外,还涉及其他几道简单的编程题目,如分数表示、字符串比较等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

总结

  • 这一年前6题简单,25min做完,卡在了第七题、第八题都没AC。
  • 第三题在声明局部变量、输出时类型转换用的时间多了点。能放全局的就放全局,能double就不用int。
  • 第五题题意读错了,时间花多了。

第七题

20min 12/20

题意

给两个整数n、k。求n的阶乘去掉末尾0后的k位数,要求补上前导零。同时输出末尾0的个数。

思路

  • 首先n上限是1E7,且k最大为9。则求阶乘时必须用long long。
  • 然后,为方便取前k为数,设置mod = pow(10, k)。
  • 每次循环把2和5的因子存起来,乘出来后取模。
  • 最后乘上所有的2和5.

小结

这里注意错解中,直接相乘可能由于某一连续区间,5的因子过多而2的因子过少,导致0过多从而丢失精度

题解 - 待测试

#include<bits/stdc++.h>
using namespace std;
#define int long long
string b;
int n, k, cnt, ans = 1, mod, tmp, c2, c5;
signed main(){
	cin>>n>>k;
	mod = pow(10, k);
	for(int i = 2; i <= n; i ++){
		int tar = i;
		while(tar % 5 == 0){
			tar /= 5;
			c5 ++;
		}
		while(tar % 2 == 0){
			tar /= 2;
			c2 ++;
		}
		ans = (ans * tar) % mod;
	}
	int c = min(c2, c5);
	for(int i = 0; i < c2 - c; i ++) ans = (ans * 2) % mod;
	for(int i = 0; i < c5 - c; i ++) ans = (ans * 5) % mod;
	b = to_string(ans);
	while(b.size() < k) b = "0" + b;
	cout<<b<<' '<<c;
	return 0;
}

错解

#include<bits/stdc++.h>
using namespace std;
#define int long long
string b;
signed main(){
	int n, k, tmp, cnt = 0, ans = 1;
	cin>>n>>k;
	for(int i = 2; i <= n; i ++){
		ans *= i;
		while(ans % 10 == 0){
			ans /= 10;
			cnt++;
		}
		b = to_string(ans);
		if(b.size() > k) b = b.substr(b.size() - k);
		ans = stoi(b);
	}
	while(b.size() < k)b = "0" + b;
	cout<<b<<' '<<cnt;
	return 0;
}

第八题 - 二分查找

15min 16/25

题意

给定数组大小n和一个数字p,然后给出n个数字的集合。
从集合中取2个数字,能和p一起构成三角形,问这样的组合数。

思路

暴力两层循环能拿16分:

  • 先得到数组rec
  • sort排序使rec从小到大
  • 然后ij指针两层循环。每次判断三个数字能否构成三角形。
    二分AC:
  • 同样先得到rec并排好序。
  • rec最后插入一个无穷大INT_MAX以简化后续二分逻辑。
  • 然后指针i遍历rec,
    • 分别求出rec[i]和p构成三角形的第三条边上下限minl、maxl。
    • 二分查找第一个大于minl的数字,大于等于maxl的数字,maxl - minl表示对于下标i可以组成的组合数。

小结

  • 网上看到一个求二分的函数记录一下:
upper_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。

lower_bound( begin,end,num):从数组的begin位置到end-1位置二分查找第一个大于或等于num的数字,找到返回该数字的地址,不存在则返回end。通过返回的地址减去起始地址begin,得到找到数字在数组中的下标。
  • 二分法不熟悉,TLE后转改二分没改完就到时间了。
//求右侧性质最左端:
int l = i + 1, r = n;
while(l < r){
	int m = l + r >> 1;
	if(右侧性质成立) r = m;
	else l = m + 1;
}
//求左侧性质最右端:
int l = i + 1, r = n;
while(l < r){
	int m = l + r + 1 >> 1;
	if(左侧性质成立) l = m;
	else r = m - 1;
}
  • 注意cnt需要开longlong,比如100000个元素包括q全是1,总数cnt会超过INT_MAX。

题解 - AC

#include<bits/stdc++.h>
using namespace std;
#define int long long
int n, p, cnt, rec[100100];
signed main(){
	cin>>n>>p;
	for(int i = 0; i < n; i ++) cin>>rec[i];
	sort(rec, rec + n);
	for(int i = 0; i < n; i ++)
		cnt += lower_bound(rec + i + 1, rec + n, rec[i] + p) - upper_bound(rec + i + 1, rec + n, abs(rec[i] - p));
	cout<<cnt;
	return 0;
}

题解 - 待测试

#include<bits/stdc++.h>
using namespace std;
int rec[100010];
int main(){
	int n, p; cin>>n>>p;
	for(int i = 0; i < n; i ++) scanf("%d", &rec[i]);
	sort(rec, rec + n);
	rec[n] = INT_MAX;
	int cnt = 0;
	for(int i = 0; i < n - 1; i ++){
		int minl = abs(rec[i] - p), maxl = rec[i] + p;
		int l = i + 1, r = n, al, ar;
		while(l < r){
			int m = l + r >> 1;
			if(rec[m] > minl) r = m;
			else l = m + 1;
		}
		al = r;
		l = i + 1, r = n;
		while(l < r){
			int m = l + r >> 1;
			if(rec[m] >= maxl) r = m;
			else l = m + 1;
		}
		ar = r;
		cnt += ar - al;
	}
	cout<<cnt;
	return 0;
}

第一题

1min

#include<bits/stdc++.h>
using namespace std;
int main(){
	double a, b, x;
	cin>>a>>b;
	x = a / b;
	printf("%.0lf/%.1lf=%.0lf", a, x, b);
	return 0;
}

第二题

1min

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n; cin>>n;
	if(n >= 90) printf("gong xi ni kao le %d fen!", n);
	else printf("kao le %d fen bie xie qi!", n);
	return 0;
}

第三题

5min

#include<bits/stdc++.h>
using namespace std;
int main(){
	int n, a, b, l = 0, r = 0, all = 0, man = 0;
	cin>>n;
	for(int i = 0; i < n; i ++){
		cin>>a>>b;
		all += b;
		if(a == 1){
			l += b;
			man++;
		}else r += b;
	}
	if(n != man && man != 0) printf("%.1lf %.1lf %.1lf",(double)all / n, (double)l / man, (double)r / (n - man));
	else if(n == man) printf("%.1lf %.1lf X",(double)all / n, (double)l / n);
	else printf("%.1lf X %.1lf",(double)all / n, (double)r / n);
	return 0;
}

第四题

1min

#include<bits/stdc++.h>
using namespace std;
int main(){
	long long a, b;
	cin>>a>>b;
	a = a + b;
	string c = to_string(a);
	for(int i = 0; i < c.size(); i ++)
		cout<<c[i]<<endl;
	return 0;
}

第五题

7min

#include<bits/stdc++.h>
using namespace std;
int main(){
	string ans, g; cin>>ans>>g;
	while(g != "-1"){
		if(g.size() != ans.size()) puts("No");
		else{
			int sum = 0, f = 1;
			for(int i = 0; i < ans.size(); i ++){
				if(ans[i] != g[i]){
					sum ++;
					if(abs(ans[i] - g[i]) > 1) f = 0;
				}
			}
			if(sum > 1) f = 0;
			puts(f ? "Yes":"No");			
		}
		cin>>g;	
	}
	return 0;
}

第六题

8min

#include<bits/stdc++.h>
using namespace std;
vector<int> l(10);
int n, tmp, ptr, t;
string s[3] = {"ChuiZi", "JianDao", "Bu"};
int main(){
	cin>>n;
	for(int i = 0; i < n; i ++) cin>>l[i]; 
	string a, b;
	cin>>a;
	t = l.front();
	while(a != "End"){
		if(t != 0){
			for(int i = 0; i < 3; i ++)
				if(a == s[i]) cout<<s[(i + 2) % 3]<<endl;
			t--;
		}else{
			for(int i = 0; i < 3; i ++)
				if(a == s[i]) cout<<s[(i + 1) % 3]<<endl;
			ptr = (ptr + 1) % n;
			t = l[ptr];
		}
		cin>>a;
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值