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

A - TT数鸭子

描述:

在这里插入图片描述

输入输出及样例:

输入:
输入第一行包含两个数n,k,表示鸭子的个数和题目要求的k。
接下来一行有n个数, a i a_i ai,每个数表示鸭子被TT映射之后的值。

输出:
输出一行,一个数,表示满足题目描述的鸭子的个数。

样例:
输入:

6 5
123456789 9876543210 233 666 1 114514

输出:

4

思想:

long long大约可以保存19位,所以没有必要用字符串读入,直接开long long 整型数保存数字。num[i]代表i在一个数字中是否出现过,如果没出现过,该数字中不同的数字个数weinum加一,并将num[i]标记为1。判断比较weinum输出结果。while循环内通过不断模10得到各个位置的数字。

代码:

#include<bits/stdc++.h>
using namespace std;
int n,k;
int ans;
int num[10];//表示0-9的数字有没有出现过; 
long long a;
int main()
{
    
    
    cin>>n>>k;
	for(int i=0;i<n;i++){
    
    
		scanf("%lld",&a);
		//memset(num,0,sizeof(num));
		for(int i=0;i<10;i++)num[i]=0;
		int weinum=0;
		while(a>0){
    
    
			int wei=a%10;
			if(num[wei]==0){
    
    
				num[wei]=1;
				weinum++;
			}
			a=a/10;
		}
		if(weinum<k)ans++;
	}	
	printf("%d\n",ans);
	return 0;
}

B - 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

在这里插入图片描述

思想:

看一下数据组成n不超过1000,O(n^2)就是1e6,可以试试。
直接暴力枚举圆心r,计算其包围所有质点的最小半径。
第二层遍历半径,获取最大距离以便圆能够覆盖所有点。
按条件比较半径,x坐标以及y坐标。

这里注意是输出半径的平方,不是半径orz,读题不仔细,没有看清楚输入输出要求。

代码:

#include<iostream>
using namespace std;
#define ll long long
int n;
ll nowr,ansx,ansy;
ll ansr=1e12;
struct node{
    
    
	ll x,y;
}nodes[1010];

int main()
{
    
    
    scanf("%d",&n);
    for(int i=0;i<n;i++)
        scanf("%lld%lld",&nodes[i].x,&nodes[i].y);

    for(int i=0; i<n; i++){
    
    
    	nowr=0;
        for(int j=0; j<n; j++)
            if(i!=j){
    
     
            	ll dis=(nodes[i].x-nodes[j].x)*(nodes[i].x-nodes[j].x)
				      +(nodes[i].y-nodes[j].y)*(nodes[i].y-nodes[j].y);
				nowr=max(nowr,dis);
			}
			
        	if(ansr>nowr){
    
    
        		ansr=nowr;
        		ansx=nodes[i].x;
        		ansy=nodes[i].y;
			}
        	else if(ansr==nowr){
    
    
        		if(ansx>nodes[i].x || (ansx==nodes[i].x&&ansy>nodes[i].y)){
    
    
        		    ansx=nodes[i].x;
					ansy=nodes[i].y;	
				}    
			}
	}
	printf("%lld.00 %lld.00\n%lld.00",ansx,ansy,ansr);
	return 0;
}
/*
5
0 0
0 1
1 0
0 -1
-1 0
*/

C - 宇宙狗的危机

描述:

在瑞神大战宇宙射线中我们了解到了宇宙狗的厉害之处,虽然宇宙狗凶神恶煞,但是宇宙狗有一个很可爱的女朋友。最近,他的女朋友得到了一些数,同时,她还很喜欢树,所以她打算把得到的数拼成一颗树。
这一天,她快拼完了,同时她和好友相约假期出去玩。贪吃的宇宙狗不小心把树的树枝都吃掉了。所以恐惧包围了宇宙狗,他现在要恢复整棵树。
但是它只知道这棵树是一颗二叉搜索树,同时任意树边相连的两个节点的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:

16
3 6 9 18 36 108
Yes

样例2:

22
7 17
9
4 8 10 12 15 18 33 44 81
No
Yes

样例1可构造如下图:
在这里插入图片描述

思想:

动态规划区间型:
令right_tree[ i ][ j ]表示区间[ i+1,j ]是否是一个二叉搜索树(其本身可以作为一个右子树);
令left_tree[i][j]表示区间[i][j-1]是否可以作为一个二叉搜索树(其本身可以作为一个左子树);
dp[i][j]表示数a[i]和数a[j]最大公因数大于1;
如果left_tree[i][root] && right_tree[root][j],则root可以作为桥梁连接[i][root-1]和[root+1][j];
同时,如果dp[i-1][root]==1,则root与i-1之间的边合法,则整个[i][j]可以作为一个右子树;按照定义的right_tree[i-1][j]置1;左子树同理;

题目中的求最大公因数用辗转相除法;{return b == 0 ? a : gcd(b,a%b);}
在这里插入图片描述

代码:

#include<bits/stdc++.h>
using namespace std;
int t,n;
int a[710];
int left_tree[710][710];
int right_tree[710][710];
int dp[710][710];//满足gcd要求; 

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

int main()
{
    
    
	cin>>t;
	while(t--)
	{
    
    
		cin>>n;
		memset(dp, 0, sizeof(dp));
	    memset(left_tree, 0, sizeof(left_tree));
	    memset(right_tree, 0, sizeof(right_tree));
		int flag=0;
		for(int i=1;i<=n;i++) 
		    cin>>a[i];
		
		for(int i=1;i<=n;i++){
    
    //所有满足边相连要求的点的对 
			for(int j=i;j<=n;j++){
    
    
				if(gcd(a[i],a[j])>1) 
				  dp[i][j]=1,dp[j][i]=1;
			}
		}
		
		for(int i=1;i<=n;i++){
    
    
		   left_tree[i][i]=1;
		   right_tree[i][i]=1;
	    }
	    
		for(int i=n;i>0;i--){
    
    
			for(int j=i;j<=n;j++){
    
    
				
				for(int root=i;root<=j;root++)
				{
    
    //遍历根节点 
					if(left_tree[i][root] && right_tree[root][j])
					{
    
    //左右都可成树; 
						if(i==1 && j==n){
    
    //所有数均可成树; 
							flag=1;
							break;
						}
						if(dp[i-1][root]) right_tree[i-1][j]=1;
						if(dp[j+1][root]) left_tree[i][j+1]=1;
					}
				}
				if(flag) break; 
			}
			if(flag) break; 
		}
		if(flag) cout<<"Yes"<<endl;
		else cout<<"No"<<endl;
	}
	return 0;
}

Guess you like

Origin blog.csdn.net/weixin_44080131/article/details/106562670