天天看点

牛客寒假算法基础训练营5

历史性突破,第一次AC三道题,有点小激动

1、酷炫双节棍

题目描述

小希现在手里有一个连着的两块木条,长度分别为l1,l2,木条之间有一个无摩擦的连接点,木条之间可以相互转动,小希将其称之为双截棍。

现在小希把长为l1的木条的一端放在原点(0,0),任意转动这两根木条,小希想知道,是否有可能通过一种转动方式使得双截棍的另一端到达指定点呢?

如果不能,请输出所有能到达的点中离目标点最近的距离。

输入描述:

第一行输入一个两个正整数l1,l2,表示木条长度。

第二行输入一个正整数T,表示询问次数。

随后T行,每行两个实数xi,yi表示目标点的坐标。

l1,l2≤1000

T≤1000

|x|,|y|≤10000

输出描述:

对于每次询问,如果可以到达,输出0,如果无法到达,给出所有能到达的点中离目标点最近的距离。

你的答案将被认为是正确的,如果相对误差不大于1e-6。

示例1

输入

23 13

3

15 1

40 0

0 0

输出

0.00000000

4.00000000

10.00000000

送温暖的题目居然WA了14次,算是自暴自弃了直接,最后发现输入的坐标也有可能是浮点数,注意到这点两个方法就都过了。第一个方法是分情况讨论,第二个则是更精简的方法,大体做法画一下就可以出来。

方法一AC:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int l1,l2;
	cin>>l1>>l2;
	int n;
	double ans;
	cin>>n;
	while(n--)
	{
		double t1,t2;
		cin>>t1>>t2;
		long double l=sqrt(t1*t1+t2*t2);
		if(l1>=l2)
		{
			if(l>=(l1-l2)&&l<=(l1+l2))
				ans=0;	
			else if(l<(l1-l2))
				ans=l1-l2-l;
			else if(l>(l1+l2))
				ans=l-l1-l2;	
		}
		else
		{
			if(l<=l1+l2&&l>=l2-l1)
				ans=0;
			else if(l>l1+l2)
				ans=l-l1-l2;
			else
				ans=l2-l1-l;
		}
		printf("%.8lf\n",ans);
	}
	return 0;
}
           

方法二AC:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int l1,l2,n;
	double ans;
	cin>>l1>>l2>>n;
	double minl = abs(l1 - l2);
	double maxl = abs(l2 + l1);
	if(minl>maxl)
	{
		int temp=minl;
		minl=maxl;
		maxl=temp;
	}
	while(n--)
	{
		double x,y;
		cin>>x>>y;
		double l=sqrt(x*x+y*y);
		if(l<=maxl&&l>=minl)
			ans=0;
		else if(l<minl)
			ans=minl-l;
		else if(l>maxl)
			ans=l-maxl;
		printf("%.8lf\n",ans);
	}
	return 0;
}
           

官方代码用到了一个hypotl函数,作用是求两个数平方和的平方根,相当于直接求到了距离,这是一个C函数库里的函数,再次受教了

#include <bits/stdc++.h>
using namespace std;
 
int l1, l2;
 
int main() {
    int T;
    scanf("%d%d%d", &l1, &l2, &T);
    while (T--) {
        double x, y;
        scanf("%lf%lf", &x, &y);
        long double dist = hypotl(x, y);
        long double ans = 0;
        if (dist > l1 + l2) ans = dist - l1 - l2;
        if (dist < abs(l1 - l2)) ans = abs(l1 - l2) - dist;
        printf("%.12Lf\n", ans);
    }
    return 0;
}
           

2、酷炫镜子

题目描述

小希拿到了一个镜子块,镜子块可以视为一个N x M的方格图,里面每个格子仅可能安装

\

或者

/

的镜子,会反射90°光线,也可能没有安装镜子,使用

.

代替。

但她看不清楚里面的镜子构造是怎样的。

你是这块镜子块的主人,所以你想计算这块镜子块(从输入的上方往下射入光线)从左到右每一格射入依次分别会从最下面的哪一格子射出,如果无法射出,输出-1。

输入描述:

第一行输入两个整数N,M。随后的N行,每行M个字符。

1≤N,M≤500

输出描述:

输出M行整数,依次为从第i个格子从上往下射入时从下面的哪里射出,如果光线不会从下面射出,则输出-1。

示例1

输入

3 3

.

输出

3

2

-1

说明

第一列遇到镜子两次反弹通过第三列射出。

第二列直接射出。

第三列因为镜子反弹后向右边射出。

其实这道题真的没有看起来那么麻烦,一个变相的搜索而已,区别一下方向,从不同方向遇见两种镜子分别会有不同的方向偏转,当行从下方到达地图外说明可以出去,当从其他位置到达地图外则说明不能出去,根据这个进行递归搜索就可以过了。

AC代码

#include<bits/stdc++.h>
using namespace std;
int M,N;
char Map[505][505];
void search_line(int x,int y,char dir) 
{
	if(x==M)
	{
		cout<<y+1<<endl;
		return ;
	}
	if(x<0||y>=N||y<0)
	{
		cout<<"-1"<<endl;
		return;
	}
	if(Map[x][y]=='.')
	{
		if(dir=='D')
			search_line(x+1,y,dir);
		else if(dir=='L')
			search_line(x,y-1,dir);
		else if(dir=='R')
			search_line(x,y+1,dir);
		else if(dir=='U')
			search_line(x-1,y,dir);
	}
	if(Map[x][y]=='\\')
	{
		if(dir=='D')
			search_line(x,y+1,'R');
		else if(dir=='L')
			search_line(x-1,y,'U');
		else if(dir=='R')
			search_line(x+1,y,'D');
		else if(dir=='U')
			search_line(x,y-1,'L');
	}
	if(Map[x][y]=='/')
	{
		if(dir=='D')
			search_line(x,y-1,'L');
		else if(dir=='L')
			search_line(x+1,y,'D');
		else if(dir=='R')
			search_line(x-1,y,'U');
		else if(dir=='U')
			search_line(x,y+1,'R');
	}
}

int main()
{
	cin>>M>>N;
	getchar();
	for(int i=0;i<M;i++)
	{
		for(int j=0;j<N;j++)
			cin>>Map[i][j];
		getchar();
	}
	for(int i=0;i<N;i++)
		search_line(0,i,'D');
	return 0;
}
           

3、酷炫数学

题目描述

小希最近想知道一个东西,就是A+B=A|B(其中|为按位或)的二元组有多少个。

当然,直接做这个式子对小希来说太难了,所以小希改变了一些条件,她仅想知道其中A,B<N的情况,其中N为2的幂次。

当然,(A=1,B=0)和(A=0,B=1)被认为是不同的二元组。

输入描述:

第一行输入一个非负整数M。

N=2^M,M≤100

即2的M次为N。

输出描述:

一个整数ans,对998244353取模。

示例1

输入

输出

1

示例2

输入

71

输出

588378066

这是官方号称的签到题,更像是一个思维题,a+b什么时候等于a|b,题目其实给了暗示,把N化为二进制,只要二进制对应相加的位置在加之前为相异的即可,但是这样还是很抽象,可以采用笨办法就是写一下,写出来输入0,1,2的答案,对应答案为1,3,9,大胆尝试答案是等比为3的等比数列,直接求就可以了,还有一点需要注意的是数据量,需要取模,但是先算再取模会在计算过程中就溢出,所以在计算每一步的时候都取模一下,这样总量就不会溢出了。

AC代码:

#include<bits/stdc++.h>
using namespace std;
int main()
{
	int M;
	cin>>M;
	unsigned long long int ans=1;
	for(int i=0;i<M;i++) 
	ans=ans*3 %998244353;
	cout<<ans<<endl;
	return 0;
}
           

4、酷炫数字

题目描述

小希希望你构造一个最小的正整数,使得其有n个因子。

输入描述:

第一行一个整数T表示数据组数

每组数据第一行输入一个正整数n,表示其因子数。

n≤1,000,000

T≤1,000,000

输出描述:

输出一行一个整数,表示你构造出的这个数。注意:你需要保证你构造的数≤1,000,000,如果在这个范围里面无法构造出一个正整数满足条件,请输出-1。

示例1

输入

2

4

5

输出

6

16

这个题比想象中简单不少,打表居然都可以过,需要用两层循环来统计因子数目,之后直接输出就好,难点在于这个打表,外层的i用于遍历每个数,内层的j则是遍历i所有在范围内的倍数,对于这些数,它们必然有i这个因子,如果构造的数字超出了范围就表示为0x3f。

#include <bits/stdc++.h>
using namespace std;
 
const int N = 1e6 + 5;
int min_num[N], T, n;
int fact_count[N];
int main() {
    memset(min_num, 0x3f, sizeof(min_num));
    for (int i = 1; i <= 1000000; i++) {
        for (int j = i; j <= 1000000; j += i) {
            fact_count[j]++;
        }
        min_num[fact_count[i]] = min(min_num[fact_count[i]], i);
    }
    scanf("%d", &T);
    while (T--) {
        scanf("%d", &n);
        if (min_num[n] == 0x3f3f3f3f)
            puts("-1");
        else
            printf("%d\n", min_num[n]);
    }
    return 0;
}