牛客寒假算法基础集训营2 部分题解

本文精选了多个算法竞赛题目,包括平面三角形面积求解、砝码设计、字符串变换、质数构造等,通过详细解析提供了高效算法实现,涵盖了快速排序、动态规划、贪心算法等核心概念。

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

A 处女座的签到题

链接:https://ac.nowcoder.com/acm/contest/327/A
来源:牛客网
 

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

平面上有n个点,问:平面上所有三角形面积第k大的三角形的面积是多少?

输入描述:

第一行T,表示样例的个数。
对于每一组样例,第一行两个整数n和k,
接下来n行,每行两个整数x,y表示点的坐标
T<=80

3<=n<=100

-109<=x,y<=109

对于每一组样例,保证任意两点不重合,且能构成的三角形的个数不小于k

输出描述:

对于每一组样例,输出第k大三角形的面积,精确到小数点后两位(四舍五入)。

示例1

输入

复制

1
4 3
1 1
0 0
0 1
0 -1

输出

复制

0.50

说明

样例中一共能构成3个三角形,面积分别为0.5,0.5,和1,面积第3大的为0.5

分析:

nth_element是个好东西

官方题解:

由于题目条件,xy坐标的绝对值均在1e9以下,面积可能会到达1e18,所以无法用double储存。三角形的面积等于相邻两边叉积的一半,所以三角形面积的两倍一定是整数,我们可以用long long来储存,最后特判输出”.00”或”.50”

  对于找第k大,时间复杂读为O(n),可以利用快排的思想或者利用nth_element

#include <bits/stdc++.h>
using namespace std;
#define ll long long
int t,n,k;
struct point
{
    ll x,y;
} a[105];
 
int main()
{
    scanf("%d",&t);
    while (t--)
    {
        scanf("%d%d",&n,&k);
        for (int i=1; i<=n; i++)
        {
            scanf("%lld%lld",&a[i].x,&a[i].y);
        }
        vector<ll> v;
        for (int i=1; i<=n; i++)
        {
            for (int j=i+1; j<=n; j++)
            {
                for (int w=j+1; w<=n; w++)
                {
                    ll area=abs((a[j].x-a[i].x)*(a[w].y-a[i].y)-(a[w].x-a[i].x)*(a[j].y-a[i].y));
                    ///求叉积,肯定为整数为三角形面积二倍
                    v.push_back(area);
                }
            }
        }
        nth_element(v.begin(),v.begin()+v.size()-k,v.end());
        ll ans=v[v.size()-k];
        if (ans%2==0)
        	 printf("%lld.00\n",ans/2);
        else 
             printf("%lld.50\n",ans/2);
    }
    return 0;
}

C处女座的砝码

链接:https://ac.nowcoder.com/acm/contest/327/C
来源:牛客网
 

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

处女座热爱做物理实验,为了实验,处女座必须要精确的知道物品的质量。处女座准备自己设计一套砝码,每一个砝码都是正整数,这套砝码必须能够精确测量出n以内所有正整数的质量,处女座想要知道至少需要多少个砝码。你可以在天平的任意一边放置砝码。

输入描述:

 

一行,一个正整数n

1<=n<=101000

输出描述:

一个整数,表示最少的砝码数。

示例1

输入

复制

20

输出

复制

4

说明

你可以选择1,2,6,11
1=1
2=2
3=1+2
4=6-2
5=6-1
6=6
7=6+1
8=6+2
9=6+2+1
10=11-1
11=11
12=11+1
13=11+2
14=11+2+1
15=11+6-2
16=11+6-1
17=11+6
18=11+6+1
19=11+6+2
20=11+6+2+1

分析:结论题:3^k>=2*n+1

官方题解

python太秀了

 

import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Scanner;

public class Main {

	public static void main(String[] args) {
		
		Scanner in=new Scanner(System.in);
		BigInteger n =in.nextBigInteger();
		
		n=n.multiply(new BigInteger("2"));
		n=n.add(new BigInteger("1"));
		
		int l=1,r=3000;
		BigInteger three=new BigInteger("3");
		
		
		while(l<r) {
			int mid=(l+r)>>1;
			if(three.pow(mid).compareTo(n)<0) {
				l=mid+1;
			}
			else 
			{
				r=mid;
				
			}
		}
		System.out.println(l);
	}
}
import math
n = int(input())
print(math.floor(math.log(2 * n - 1) / math.log(3)) + 1)

G处女座与复读机

链接:https://ac.nowcoder.com/acm/contest/327/G
来源:牛客网
 

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

一天,处女座在牛客算法群里发了一句“我好强啊”,引起无数的复读,可是处女座发现复读之后变成了“处女座好强啊”。处女座经过调查发现群里的复读机都是失真的复读机,会固定的产生两个错误。一个错误可以是下面的形式之一:

1.       将任意一个小写字母替换成另外一个小写字母

2.       在任意位置添加一个小写字母

3.       删除任意一个字母

处女座现在在群里发了一句话,他收到了一个回应,他想知道这是不是一个复读机。

输入描述:

两行
第一行是处女座说的话s
第二行是收到的回应t
s和t只由小写字母构成且长度小于100

输出描述:

如果这可能是一个复读机输出”YES”,否则输出”NO”

示例1

输入

复制

abc
abcde

输出

复制

YES

说明

abc->abcd->abcde

示例2

输入

复制

abcde
abcde

输出

复制

YES

说明

abcde->abcdd->abcde

备注:

只要能经过两步变换就从s得到t就有可能是复读机。

分析:

暴力和DP都能解:

       暴力解法:

两步变换可以看成不小于两步变换

1.  相同,那么可以把一个字母改成错误的再改成对的

2.  改变一个字母,可以把这个字母改成错误的再改成对的

3.  增加一个字母,可以增加一个错误的字母再改成对的

4.  减少一个字母,可以先把这个字母改变再删除这个字母

然后只需要暴力所有情况即可,复杂度O(n^3)

DP解法:

我们定义dp[i][j]表示从字符串1的前i个字符转移到字符串2的前j个字符至少需要几步

DP方程则为:

dp[i][j]=min(dp[i-1][j]+1,min(dp[i][j-1]+1,dp[i-1][j-1]+flag));

方程解释:

1.dp[i-1][j]+1表示字符串1的前i个字符加1个字符到字符串2前j个字符

2.dp[i][j-1]+1表示字符串1的前i个字符-1个字符到字符串2前j个字符

3.if(s1[i]==s2[j])

dp[i-1][j-1]=dp[i][j]

else

dp[i-1][j-1]+1表示字符串1的前i个字符修改1个字符到字符串2前j个字符

暴力代码(标程)

#include <bits/stdc++.h>
using namespace std;
string a,b;
 
int main()
{
    cin>>a>>b;
    if (fabs((int)a.length()-(int)b.length())>2)
    {
        puts("NO");
    }
    else
    {
        if (b.length()==a.length()+2)
        {
            int can=0;
            for (int i=0;i<b.length();i++)
            {
                for (int j=i+1;j<b.length();j++)
                {
                    string s="";
                    for (int k=0;k<b.length();k++)
                    {
                        if (k!=i && k!=j) s.push_back(b[k]);
                    }
                    if (a==s) can=1;
                }
            }
            if (can==1) puts("YES");
            else puts("NO");
        }
        else if ((int)b.length()==(int)a.length()-2)
        {
            swap(a,b);
            int can=0;
            for (int i=0;i<b.length();i++)
            {
                for (int j=i+1;j<b.length();j++)
                {
                    string s="";
                    for (int k=0;k<b.length();k++)
                    {
                        if (k!=i && k!=j) s.push_back(b[k]);
                    }
                    if (a==s) can=1;
                }
            }
            if (can==1) puts("YES");
            else puts("NO");
        }
        else if (b.length()==a.length()+1)
        {
            int can=0;
            for (int i=0;i<b.length();i++)
            {
                string s="";
                for (int j=0;j<b.length();j++)
                {
                    if (j!=i) s.push_back(b[j]);
                }
                int cnt=0;
                for (int i=0;i<a.length();i++)
                {
                    if (a[i]!=s[i]) cnt++;
                }
                if (cnt<=1) can=1;
            }
            if (can==1) puts("YES");
            else puts("NO");
        }
        else if ((int)b.length()==(int)a.length()-1)
        {
            swap(a,b);
            int can=0;
            for (int i=0;i<b.length();i++)
            {
                string s="";
                for (int j=0;j<b.length();j++)
                {
                    if (j!=i) s.push_back(b[j]);
                }
                int cnt=0;
                for (int i=0;i<a.length();i++)
                {
                    if (a[i]!=s[i]) cnt++;
                }
                if (cnt<=1) can=1;
            }
            if (can==1) puts("YES");
            else puts("NO");
        }
        else
        {
            int can=0;
            for (int i=0;i<b.length();i++)
            {
                for (int j=i+1;j<b.length();j++)
                {
                    int cnt=0;
                    for (int k=0;k<b.length();k++)
                    {
                        if (k==i || k==j) continue;
                        if (a[k]!=b[k]) cnt++;
                    }
                    if (cnt==0) can=1;
                }
            }
            for (int i=0;i<b.length();i++)
            {
                for (int j=0;j<b.length();j++)
                {
                    string s="";
                    string t="";
                    for (int k=0;k<b.length();k++)
                    {
                        if (k!=i) s.push_back(a[k]);
                    }
                    for (int k=0;k<b.length();k++)
                    {
                        if (k!=j) t.push_back(b[k]);
                    }
                    if (s==t) can=1;
                }
            }
            if (can==1) puts("YES");
            else puts("NO");
        }
    }
    return 0;
}

DP代码

#include<bits/stdc++.h>
using namespace std;
const int INF=1e9;
const int maxn=105;
int dp[maxn][maxn];
char s[maxn],t[maxn];
int main(){
    scanf("%s %s",s+1,t+1);
    int len1=strlen(s+1),len2=strlen(t+1);
    ///dp[i][j]表示从字符串1的前i个字符转移到字符串2的前j个字符至少需要几步
    for(int i=1;i<=len1;i++){
        for(int j=1;j<=len2;j++)
        	 dp[i][j]=INF;
    }
    for(int i=1;i<=len1;i++)
        dp[i][0]=i;
    for(int j=1;j<=len2;j++) 
    	dp[0][j]=j;
    for(int i=1;i<=len1;i++){
        for(int j=1;j<=len2;j++)
        {
            int flag=1;
            if(s[i]==t[j])  flag=0;
            
            dp[i][j]=min(dp[i-1][j]+1,min(dp[i][j-1]+1,dp[i-1][j-1]+flag));
        }
    }
    if(dp[len1][len2]<=2)    
    	printf("YES\n");
    else    printf("NO\n");
}

 

H 处女座的测验(一)

链接:https://ac.nowcoder.com/acm/contest/327/H
来源:牛客网
 

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
Special Judge, 64bit IO Format: %lld

题目描述

处女座进行了一场c语言的考试,要求很简单,输出2000个正整数,并且满足以下条件:

1.       任意两个数互质

2.     任意两个数x,y,满足,其中为n的因子的个数

 

举例:6的因子有1,2,3,6,所以τ(6)=4τ(6)=4

输入描述:

本题没有输入

输出描述:

 

2000行,每行一个正整数

输出的每个整数都必须在1-4*108之间

如果有多组答案,输出任意一组即可。

分析:我们知道一个质数怕p1有两个因子,两个质数p1,p2相乘有四个因子:1,p1,p2,p1*p2

....所以四个质数相乘就会有16个因子,即我们用x=p1*p2,y=p3*p4,我们用四千个质数就可以构造2000个这样的数。但要第一个乘以第4000个,可以算出

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 20007
using namespace std;
typedef long long ll;
const int MAXN =40000;
bool flag[MAXN];
int primes[MAXN/3],pi;
void Prime()
{
    int i,j;
    pi=0;
    memset(flag,false,sizeof(flag));
    for(i=2;i<MAXN;i++)
    {
        if(!flag[i]) primes[++pi] = i;
        for(j=1;(j<=pi)&&(i*primes[j]<MAXN);j++)
        {
            flag[i*primes[j]]=true;
            if (i%primes[j]==0)
                break;
        }
    }
}
int main()
{
    Prime();
    int cnt=0;
    int num=0;
    for(int i=1;i<=2000;i++)
    {
        ll ans=primes[i]*primes[4001-i];

          cout<<ans<<endl;
    }
    return 0;
}

J处女座的期末复习

链接:https://ac.nowcoder.com/acm/contest/327/J
来源:牛客网
 

时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 262144K,其他语言524288K
64bit IO Format: %lld

题目描述

快要期末考试了,处女座现在有n门课程需要考试,每一门课程需要花ai小时进行复习,考试的起始时间为bi,处女座为了考试可以不吃饭不睡觉,处女座想知道他能否复习完所有的科目(即在每一门考试之前复习完该科目)。每一门课的考试时间都为两小时。

输入描述:

 

第一行一个整数n

第二行n个整数a1,a2,…,an,表示每门课需要复习的时间

第三行n个整数b1,b2,…,bn,表示每门课考试的时间

1<=n<=105

0<=ai<=109

0<=bi<=109

输出描述:

如果处女座能复习完,输出”YES”,否则输出”NO”

示例1

输入

复制

3
0 1 1
2 6 4

输出

复制

YES

说明

在0-1小时复习第2门课,
在1-2小时复习第3门课,
在2-4小时考第1门课,
在4-6小时考第3门课,
在6-8小时考第2门课

备注:

考试时不能复习,保证考试时间不会重叠。

复习可以拆开,只要复习时间够了即可。

 

分析:数据比较水

贪心做法, 每次复习还没有复习但是最先考试的科目即可。

#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <cstring>
#define N 500010
#define ll long long
using namespace std;
 

ll n,m;
struct node
{
	ll s,e;
}a[N];
bool cmp(node a,node b)
{
	return a.e<b.e;
}
int main()
{
    scanf("%lld",&n);
    
    for (int i=1;i<=n;i++)
	{
	 scanf("%lld",&a[i].s);
	}
	for (int i=1;i<=n;i++)
	{
	 scanf("%lld",&a[i].e);
	}
	sort(a+1,a+n+1,cmp);
	int t=0;
	int flag=1;
	int j=1;
	int i=1;
	for(i=1;i<=n;i++)
	{
	 t+=a[i].s;
	 if(t>a[i].e)
	 {
	 	cout<<"NO"<<endl;
	 	return 0;
	 }
	 t+=2;
	}
	
		cout<<"YES"<<endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值