程序设计思维与实践 CSP-M4 补题(week16)

本周的月考模拟题是面向T1、T2、T4的训练,在两小时内终于完成了第一、二题的满分,得到了4次测试来的一次最好成绩。最后一题本来也还是留了近一个小时来解决,但是方向错了,连部分分也未能得到,继续加油!

TT数鸭子

时间限制空间限制
1S256MB
题目描述

这一天,TT因为疫情在家憋得难受,在云吸猫一小时后,TT决定去附近自家的山头游玩。

TT来到一个小湖边,看到了许多在湖边嬉戏的鸭子,TT顿生羡慕。此时他发现每一只鸭子都不一样,或羽毛不同,或性格不同。TT在脑子里开了一个map<鸭子,整数> tong,把鸭子变成了一些数字。现在他好奇,有多少只鸭子映射成的数的数位中不同的数字个数小于k。

输入描述

输入第一行包含两个数n,k,表示鸭子的个数和题目要求的k。

接下来一行有n个数, a i a_i ai,每个数表示鸭子被TT映射之后的值。

输出描述

输出一行,一个数,表示满足题目描述的鸭子的个数。
无行末空格

样例输入

6 5
123456789 9876543210 233 666 1 114514

样例输出

4

数据组成
数据点tn a i a_i ai
1n<=1000k=10 1 0 6 10^6 106
2n<=1000k=1 1 0 6 10^6 106
3,4,5n<= 1 0 5 10^5 105k<=100 1 0 9 10^9 109
6,7,8,9,10n<= 1 0 6 10^6 106k<= 1 0 6 10^6 106 1 0 15 10^{15} 1015
分析

此题较为简单。先分割字符串得到单个字符,然后将字符串中的单个字符进行排序,再对字符串中的每个字符进行遍历,当下一个字符不等于上一个字符时,计数cnt++,如果单个字符串中最终得到cnt的值<k则为一个满足条件的结果,ans++,最终得到的ans值即为答案。

C++

#include<bits/stdc++.h>
using namespace std;
int n, k, len = 0, ans = 0, cnt = 1;
char a[1000010][20];
char c;
int main() {
	cin >> n >> k;
	getchar();//吃掉回车
	for (int i = 0; i < n; i++) {
		len = 0;
		cnt = 1;
		while ((c = getchar()) != ' ') {
			if ('\n' == c) break;
			a[i][len++] = c;
		}
		sort(a[i], a[i] + len);//排序
		for (int j = 1; j < len; j++) {
			if (a[i][j] != a[i][j - 1]) cnt++;//不相等时,计数++
		}
		if (cnt < k) ans++;
	}
	cout << ans;

	return 0;
}

ZJM要抵御宇宙射线

时间限制空间限制
1S256MB
题目描述

据传,2020年是宇宙射线集中爆发的一年,这和神秘的宇宙狗脱不了干系!但是瑞神和东东忙于正面对决宇宙狗,宇宙射线的抵御工作就落到了ZJM的身上。假设宇宙射线的发射点位于一个平面,ZJM已经通过特殊手段获取了所有宇宙射线的发射点,他们的坐标都是整数。而ZJM要构造一个保护罩,这个保护罩是一个圆形,中心位于一个宇宙射线的发射点上。同时,因为大部分经费都拨给了瑞神,所以ZJM要节省经费,做一个最小面积的保护罩。当ZJM决定好之后,东东来找ZJM一起对抗宇宙狗去了,所以ZJM把问题扔给了你~

输入描述

输入 第一行一个正整数N,表示宇宙射线发射点的个数

接下来N行,每行两个整数X,Y,表示宇宙射线发射点的位置

输出描述

输出包括两行

第一行输出保护罩的中心坐标x,y 用空格隔开
第二行输出保护罩半径的平方
(所有输出保留两位小数,如有多解,输出x较小的点,如仍有多解,输入y较小的点)
无行末空格

样例输入

5
0 0
0 1
1 0
0 -1
-1 0

样例输出

0.00 0.00
1.00

数据组成
数据点txy
1~5n<=100|x|<=10000|y|<=10000
6~10n<=1000|x|<=100000|y|<=100000
分析

此题也比较简单。因为中心位于一个宇宙射线的发射点上,所以进行一点到其他所有的点的距离的遍历,得到最大距离作为一个以这个点为圆心的应该的半径值。再在以所有这些点为中心的圆的半径中选出半径最小的那个中心点的中心坐标x,y 作为结果的中心坐标x,y ,再输出半径的平方,具体排序选择出的最终结果可通过结构体的自定义排序可以得到。

C++

#include<bits/stdc++.h>
#include <iomanip>
using namespace std;
int n;
double maxDis=0.0;
struct point{
	double x,y,mx;//注意都要用double
	bool operator <(const point &b)const{//自定义排序
		if(mx!=b.mx)return mx<b.mx;
		else
			return x==b.x? y<b.y:x<b.x;
	}
}points[1010];

int main() {
	cin>>n;
	for(int i = 0;i < n;i++){
		cin>>points[i].x>>points[i].y;
	}
	for(int i = 0;i<n;i++){
		for(int j = 0;j<n;j++){//遍历得到需要的半径的最大值
			if(j!=i){
				double dis = (points[i].x-points[j].x)*(points[i].x-points[j].x)+(points[i].y-points[j].y)*(points[i].y-points[j].y);
				maxDis = max(maxDis,dis);
			}
		}
		points[i].mx = maxDis;
		maxDis = 0.0;
	}
	sort(points,points+n);//对点进行排序后输出
	cout<<setiosflags(ios::fixed)<<setprecision(2)<<points[0].x<<" "<<points[0].y<<endl;//输出格式
	cout<<setiosflags(ios::fixed)<<setprecision(2)<<points[0].mx;

}

宇宙狗的危机

时间限制空间限制
5S256MB
题目描述

在瑞神大战宇宙射线中我们了解到了宇宙狗的厉害之处,虽然宇宙狗凶神恶煞,但是宇宙狗有一个很可爱的女朋友。

最近,他的女朋友得到了一些数,同时,她还很喜欢树,所以她打算把得到的拼成一颗

这一天,她快拼完了,同时她和好友相约假期出去玩。贪吃的宇宙狗不小心把树的树枝都吃掉了。所以恐惧包围了宇宙狗,他现在要恢复整棵树,但是它只知道这棵树是一颗二叉搜索树,同时任意树边相连的两个节点的 gcd(greatest common divisor) 都超过1。

但是宇宙狗只会发射宇宙射线,他来请求你的帮助,问你能否帮他解决这个问题。

补充知识
GCD:最大公约数,两个或多个整数共有约数中最大的一个 ,例如8和6的最大公约数是2。
一个简短的用辗转相除法求gcd的例子:

int gcd(int a,int b){return b == 0 ? a : gcd(b,a%b);}
输入描述

输入第一行一个t,表示数据组数。
对于每组数据,第一行输入一个n,表示数的个数
接下来一行有n个数 a i a_i ai,输入保证是升序的。

输出描述

每组数据输出一行,如果能够造出来满足题目描述的树,输出Yes,否则输出No。
无行末空格。

样例输入1

1
6
3 6 9 18 36 108

样例输出1

Yes

样例输入2

2
2
7 17
9
4 8 10 12 15 18 33 44 81

样例输出2

No
Yes

样例解释

样例1可构造如图!Alt

数据组成

给出的数为上限。

数据点数tn a i a_i ai
1,2,3515 1 0 9 10^9 109
4,5,6535 1 0 9 10^9 109
7,8,9,105700 1 0 9 10^9 109
分析

这道题是一道动态规划题目,测试的时候完全没有发现。开始还想通过数据结构中搜索二叉树的构造下手,但是根节点完全没有确定的方法……用动态规划解题过程如下:

  • 定义dp状态L[i][j]和R[i][j],分别表示[i,j-1]能成为j的左子树、[i+1,j]能成为i的右子树。开始时初始化L[i][i]=R[i][i]=1。二维数组f[i][j]表示"gcd(a[i],a[j])>1"。
  • 从后往前遍历l,同时r范围为[l,n],k在[l,r]范围内变化,当在[l,r]范围内有任意的k值满足L[l][k]&&R[k][r]同时满足f[l-1][k](向左扩展)或者f[r+1][k](向右扩展),则更新L和R数组,最终最后检查是否有k满足L[k][1]&&R[k][n],有则输出Yes。
  • 因为n<700,所以O(n3)的复杂度可以接受,用到了三个for循环。

C++

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=750;
int t;
int n,a[M],f[M][M],L[M][M],R[M][M];
int flag=0;
int gcd(int a,int b)
{
   return b==0?a:gcd(b,a%b);
} 
int main() {
	ios::sync_with_stdio(false);
	cin>>t;
	while(t--) {
		flag=0;
		memset(L,0,sizeof(L));
		memset(R,0,sizeof(R));
		memset(f,0,sizeof(f));
		cin>>n;
		for(int i=1; i<=n; i++) cin>>a[i],L[i][i]=R[i][i]=1;//初始状态
		for(int i=1; i<=n; i++)
			for(int j=1; j<=n; j++)
				if(gcd(a[i],a[j])>1) f[i][j]=1;//标记符合gcd条件
		for(int l=n; l>=1; l--)
			for(int r=l; r<=n; r++)
				for(int k=l; k<=r; k++)
					if(L[l][k]&&R[k][r]) {
						if(l==1&&r==n) {
							flag=1;//当最终的k在[1,n]有值满足L[l][k]&&R[k][r],即该点构建出的左右子树都可以满足题目条件
							break;
						}
						//只要在[l,r]范围内有一个k满足条件即可扩展
						if(f[l-1][k]) R[l-1][r]=1;//符合f为1的条件向左扩展,增加右子树
						if(f[r+1][k]) L[l][r+1]=1;//符合f为1的条件向右扩展,增加左子树
					}
		if(flag==1)printf("Yes\n");
		else
			printf("No\n");
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值