2022牛客寒假算法基础集训营4

C

蓝彗星

题意:有n颗蓝彗星和红彗星,每颗星持续的时间都为t,给定每颗星星出现的时间,求只能看见蓝彗星的时长。

思路:求出有星星的时间和红星星持续的时间,只能看见蓝星星的时长=有星星的时间-红星星持续的时间。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 1e5 + 10;
const int inf=0x3f3f3f3f;
int n,t;
struct node{
    char col;
    int start,end;
}a[maxx];
bool cmp(node a,node b)
{
    if(a.start==b.start) return a.end<b.end;
    return a.start<b.start;
}
void solve()
{
    scanf("%d %d",&n,&t);
    getchar();
    for(int i=1;i<=n;i++) scanf("%c",&a[i].col);
    //for(int i=1;i<=n;i++) cout<<a[i].col<<' ';
    //cout<<'\n';
    for(int i=1;i<=n;i++) 
    {
        scanf("%d",&a[i].start);
        a[i].end=a[i].start+t;
    }
    sort(a+1,a+n+1,cmp);
    int ans=0;
    int last=-1,T=0;
    for(int i=1;i<=n;i++)//区间合并
    {
        if(a[i].start>last)
        {
            T+=a[i].end-a[i].start;
            last=a[i].end;
        }
        else if(a[i].end>last)
        {
            T+=a[i].end-last;
            last=a[i].end;
        }
    }
    last=-1;
    int Tr=0;
    for(int i=1;i<=n;i++)//区间合并
    {
        if(a[i].col=='R')
        {
            if(a[i].start>last)
            {
                Tr+=a[i].end-a[i].start;
                last=a[i].end;
            }
            else if(a[i].end>last)
            {
                Tr+=a[i].end-last;
                last=a[i].end;
            }
        }
    }
    ans=T-Tr;
    printf("%d",ans);
}
int main()
{
    int _t=1;
    //scanf("%d", &_t);
    while (_t--)
    {
        solve();
    }
    system("pause");
}

差分与前缀和的做法:

#include<bits/stdc++.h>
using namespace std;
int n,k;
string s;
int sum1[202020],sum2[202020];//蓝星星,红星星
int main(){
    int i,x;
    cin>>n>>k;
    string s;
    cin>>s;
    for(i=0;i<n;i++){
        cin>>x;
        if(s[i]=='B')sum1[x]++,sum1[x+k]--;//x时刻蓝星星++,x+k时刻蓝星星--
        else sum2[x]++,sum2[x+k]--;//x时刻红星星++,x+k时刻红星星--
    }
    int res=0;
    //求前缀和
    for(i=1;i<=2e5;i++)sum1[i]+=sum1[i-1],sum2[i]+=sum2[i-1],res+=(sum1[i]&&!sum2[i]);//只有蓝,答案++
    cout<<res;
}

D

雪色光晕

题意:求固定点到折线的最短距离,可以拆分成点到线段的最短距离

思路:点到线段的最短距离为以下两种情况中的一种:点到直线的最短距离,点到线段两端的最短距离。当点向直线作垂线,垂足不在线段上时为第二种情况。

如何判断第二种情况?把点C和线段两个端点A,B连接成三角形。当角A或角B为钝角时,为第二种情况。

如何求点C到直线AB的距离h?AB*h/2=S

三角形面积可以直接用海伦公式: S=\sqrt{p*(p-a)*(p-b)*(p-c)},  p=(a+b+c)/2。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 2e5 + 10;
const int inf=0x3f3f3f3f;
struct point{
    double x,y;
    point(double x,double y):x(x),y(y){}
};

double dis(point A,point B)//点AB距离
{
    return sqrt((A.x-B.x)*(A.x-B.x)+(A.y-B.y)*(A.y-B.y));
}
double S(point A,point B,point C)//三角形ABC面积
{
    double a=dis(B,C),b=dis(A,C),c=dis(A,B);//三条边长
    double p=(a+b+c)/2.0;
    return sqrt(p*(p-a)*(p-b)*(p-c));
}
bool judge(point A,point B,point C)//判断A是否是钝角
{
    double a=dis(B,C),b=dis(A,C),c=dis(A,B);
    if(a*a>b*b+c*c) return true;
    else return false;
}
double fun(point A,point B,point C)//点C到线段AB的距离
{
    if(judge(A,B,C)||judge(B,A,C))//若A为钝角或B为钝角
    {
        double d1=dis(C,A),d2=dis(C,B);//点C到线段的两端点的距离
        return min(d1,d2);
    }
    else//点C到直线AB的距离
    {
        double s=S(A,B,C);//求三角形ABC的面积
        double d3=2.0*s/dis(A,B);//点C到直线AB的距离
        return d3;
    }
}
void solve()
{
    int n;
    double x0,y0,x,y;//起点,固定点
    cin>>n;
    cin>>x0>>y0>>x>>y;
    point start(x0,y0);
    point st(x,y);
    double ans=inf;
    for(int i=1;i<=n;i++)
    {
        double xi,yi;
        cin>>xi>>yi;
        point tem(start.x+xi,start.y+yi);
        ans=min(ans,fun(start,tem,st));//点st到线段start-tem的距离
        start=tem;//更新起点
    }
    printf("%.8f",ans);
}
int main()
{
    int _t=1;
    //scanf("%d", &_t);
    while (_t--)
    {
        solve();
    }
    system("pause");
}

F

小红的记谱法

题意:第二种记谱法和第一种不同的在于,尖括号’<‘代表后面所有的音比前面的音低 8 度。反尖括号'>'则代表后面所有的音比前面的音高 8 度。

思路:用一个变量统计当前八度的情况,然后对应哪个音直接输出即可,后面根据八度的情况来添加对应数量的'.'或者'*'即可。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 1e5 + 10;
const int inf=0x3f3f3f3f;
int n,t;
string s[2]={{"01234567"},{"0CDEFGAB"}};
void solve()
{
    string ss;
    cin>>ss;
    int std=0;
    int len=ss.length();
    for(int i=0;i<len;i++)
    {
        if(ss[i]=='<') std--;
        else if(ss[i]=='>') std++;
        else
        {
            switch(ss[i])
            {
                case 'C' :printf("1");break;
                case 'D' :printf("2");break;
                case 'E' :printf("3");break;
                case 'F' :printf("4");break;
                case 'G' :printf("5");break;
                case 'A' :printf("6");break;
                case 'B' :printf("7");break;
            }
            if(std>0)
            {
                for(int j=0;j<std;j++) printf("*");
            }
            else if(std<0)
            {
                for(int j=0;j<-std;j++) printf(".");
            }
        }
    }
}
int main()
{
    int _t=1;
    //scanf("%d", &_t);
    while (_t--)
    {
        solve();
    }
    system("pause");
}

H

真真真真真签到题

思路:显然小紫选择正方体的中心,小红选择正方体的一角。题目转化成已知正方体体对角线的一半,求正方体体积。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 1e6 + 10;
const int inf=0x3f3f3f3f;


void solve()
{
    int x;
    cin>>x;
    double a=2.0*x/sqrt(3);
    printf("%.7f",a*a*a);
    
}
int main()
{
    int _t=1;
    //scanf("%d", &_t);
    while (_t--)
    {
        solve();
    }
    system("pause");
}

I

爆炸的符卡洋洋洒洒

题意:n个物品,每个物品的花费是ai,价值bi。求花费为k的倍数的时候最大价值

思路:01背包。dp[i][j]表示考虑前i个物品,花费模k为j的情况下最大价值。注意负数取模时先+k再模k

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 1010;
const int inf=0x3f3f3f3f;
int n,k;
int a[maxx];
int b[maxx];//a[i]是花费 b[i]是价值
ll dp[maxx][2020];//dp[i][j]表示考虑前i个物品,花费模k为j的情况下最大价值
void solve()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        scanf("%d %d",&a[i],&b[i]);
        a[i]%=k;
    }
    //初始化为负无穷,表示无法取到
    for(int i=0;i<=n;i++)
    {
        for(int j=0;j<k;j++)
        {
            dp[i][j]=-1e16;
        }
    }
    dp[0][0]=0;
    for(int i=1;i<=n;i++)
    {
        for(int j=0;j<k;j++)
        {
            dp[i][(j+a[i])%k]=max(dp[i-1][(j+a[i])%k],dp[i-1][j]+b[i]);
            //dp[i][j]=max(dp[i-1][j],dp[i-1][(j-a[i]%k+k)%k]+b[i]);//max(不取第i件,取第i件)注意负数取模时先+k再模k
        }
    }
    if(dp[n][0]<=0) printf("-1");
    else printf("%lld",dp[n][0]);
}
int main()
{
    int _t=1;
    //scanf("%d", &_t);
    while (_t--)
    {
        solve();
    }
    system("pause");
}

K

小红的真真假假签到题题

题意:给定x,构造x的倍数y,且在二进制下,x是y的子串,x与y二进制表示中1的个数不同。

思路:注意x<=1e9,y<=1e19,1e9转化成二进制占30位,容易想到把x在二进制下重复写两次,最多占60位二进制(符合y<=1e19条件)。接下来证明此时y是x的倍数:把x在二进制下重复写两次即在x后补上若干个0后加上x。在x后补若干0即x乘若干个2,再加上x,结果y就是x的倍数。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxx = 1e6 + 10;
const int inf=0x3f3f3f3f;
ll x;

void solve()
{
    scanf("%lld",&x);
    int i=0;
    //在x后补上 x在二进制下位数个 0
    for(i=0;i<=30;i++) 
    {
        if(1ll<<i>x) break;
    }
    cout<<x*(1ll<<i)+x;//补0加x
    
}
int main()
{
    int _t=1;
    //scanf("%d", &_t);
    while (_t--)
    {
        solve();
    }
    system("pause");
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值