天天看點

牛客寒假算法基礎訓練營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;
}