2024牛客寒假算法基础集训营1- ABCEGM

本文围绕牛客竞赛中的A、B、C、E、G、M题,展示了如何运用字符串匹配、动态规划、前缀和计算解决实际问题,适合算法爱好者和编程挑战者参考。
摘要由CSDN通过智能技术生成

​​​​​​​牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ (nowcoder.com)

竞赛链接⬆

A题:

思路:

        先构建一个string数组,将所需要匹配到的字符串作为元素,依次放入数组中,根据所需要查找字符串的个数,创建一个大循环,在创建一个指针,指向string数组中所需要比较的元素,之后再依次遍历源字符串,同时于string数组中字符串元素进行比较,当有一个字符元素匹配成功,这指向string数组元素的指针向后挪动,(巧妙解决了匹配字符串在源字符串中顺序匹配的问题,如sddfs之类),之后根据匹配结果进行输出。

代码: 

        时间复杂度o(n)

#include <iostream>
using namespace std;

void solve()
{
    int n;
    cin>>n;
    string str;
    cin>>str;
    string a[]={"DFS","dfs"};
    for(int j=0;j<2;j++)
    {
        int k=0;
        for(int i=0;i<n;i++)
        {
            if(str[i]==a[j][k]) k++;
        }
         cout<< k/3<<' ';
    }
    cout<<endl;
}

int main()
{
    int t;
    cin>>t;
    while (t--)
    {
        solve();
    }

    return 0;
}

B题:

思路:

        当着火点为零的时候,最少添加为3结束。

        当着火点不为零的时候,着火点只可能是0 1 2 3 这几种情况。

        构造一个map将输入的着火点位置进行布尔捆绑,将每一次的着火点置为true,同时将着火点存储下来,进行遍历,由于(2,0)点的特殊性,将遍历分为两部分,即y<0与y>0  ,在遍历过程中,寻找每一个着火点距离为1的位置进行匹配,同时记录下,匹配上的(l,r),没匹配上的(l1,r1) ,遍历结束之后,开始分情况考虑,

  • 左右两边都有匹配上的,
  • 左边匹配上了,右边没匹配上 ,2,0处有无着火点  
  • 左边没匹配上,右边匹配上了     2,0处有无着火点
  • 左边没匹配上  ,右边也没有匹配上, 考虑 当左边的点在1,-1时,右边点在1 ,1时
  • 左边没匹配上,右边没有值,考虑左边点的位置,以及2,0出有无着火点
  • 左边没值,右边没匹配上,同上,
  • 当只有2,0处有值时

代码:

        时间复杂度o(n)

#include <iostream>
#include <map>
#include <algorithm>
using namespace std;
const int N=1e5+10;
pair<int,long long> g[N];
map<pair<int,long long>,bool> t;

void solve()
{
    t.clear();
    
    int n;
    cin>>n;
    for(int i=0;i<n;i++)
    {
        int l;long long r;
        scanf("%d %lld",&l,&r);
        g[i]={l,r};
        t[{l,r}]=true;
    }
    if(n==0) {cout<<3<<endl; return ;}
    int l,r; l=r=0;
    int l1,r1; l1=r1=0;
    for(int i=0;i<n;i++)
    {
        int x=g[i].first;long long y=g[i].second;
        if(y<0 && !l)
        {
            l1=1;
            if(x==1)
                if(t[{x+1,y}] || t[{x+1,y-1}] || t[{x+1,y+1}] ) l=1;
            else 
                if(t[{x-1,y}] || t[{x-1,y-1}] || t[{x-1,y+1}] ) l=1;
        }
        else if(y>0 && !r)
        {
            r1=1;
            if(x==1)
                if(t[{x+1,y}] || t[{x+1,y-1}] || t[{x+1,y+1}] ) r=1;
            else 
                if(t[{x-1,y}] || t[{x-1,y-1}] || t[{x-1,y+1}] ) r=1;
        }
    }
    if(l && r) cout<<0<<endl;
    else if(l)
    {
        if(r1)  cout<<1<<endl;
        else if(t[{2,0}]) cout<<1<<endl;
        else cout<<2<<endl;
    }
    else if(r)
    {
        if(l1) cout<<1<<endl;
        else if(t[{2,0}]) cout<<1<<endl;
        else cout<<2<<endl;
        
    }
    else if(l1 && r1)
    {
        if(t[{1,-1}] && t[{1,1}]) cout<<1<<endl;
        else cout<<2<<endl;
    }
    else if(l1) 
    {
         if(t[{2,0}]) cout<<2<<endl;
        else if(t[{1,-1}]) cout<<2<<endl;
        else cout<<3<<endl;
    }
    else if(r1)
    {
         if(t[{2,0}]) cout<<2<<endl;
        else if(t[{1,1}]) cout<<2<<endl;
        else cout<<3<<endl;
    }
    else cout<<2<<endl;
        
    
}

int main()
{
    int t;
    cin>>t;
    while (t--)
    {
        solve();
    }
    return 0;
}

C题:

        

思路:

        本题需要考虑数值大小问题,工作人员容忍度最大为10的18次方,所以需要开long long 储存

 根据题意,一个窗口,n个人,时间一直增加,所以每个人办完事的时刻,为一个前缀和

       注:因为 n最大值为10的5次方,t的最大值为10的6次方,最坏情况下,10的5次方个10的6次方相加,10的11次方,所以 前缀和也需要开long long 储存

        计算容忍度,可以容忍几个人增加tc个时间,也就是看它能容忍几个tc 即 q=M/tc 

  • 如果 q大于等于n的话,则安排鸡第一个,直接输出tc
  • 如果q小于n的话,则输出n-q的前缀和

代码:

        时间复杂度o(n)

#include <cstdio>
#include <iostream>
#include <algorithm>

using namespace std;
const int N=1e6+10;
long long  e[N];
int main()
{
    long long n,q,t;
    scanf("%lld%lld%lld",&n,&q,&t);
    for(int i=1;i<=n;i++) scanf("%d",&e[i]);
    sort(e+1,e+n+1);
    for(int i=1;i<=n+1;i++)  e[i]=e[i-1]+e[i];
    while (q--)
    {
        long long r;
        scanf("%lld",&r);  
        long long q=r/t;
        if(q>=n)cout<<t<<endl;
        else cout<<e[n-q]+t<<endl;
    }
    return 0;
}

E题:

思路:

        已知 m场次的最大值为10 所以可以用dfs写

        记录下每一场的选手,暴力遍历所有可能性,你只管遍历‘,剩下的交给系统

        将答案ans 初始化为1号选手最差名次为 10,之后每一次场次模拟完之后,计算1号选手的名次,之后取最小值即可。

代码:

        时间复杂度o(n^3)

#include <iostream>
using namespace std;
const int N=15;
int a[N];
pair<int,int> q[N];
int n,m;
int ans;
void dfs(int u)
{
    if(u==m)
    {
       int  res=1;
        for(int i=1;i<n;i++)
        {
            if(a[i]>a[0]) res++;
        }
        ans=min(res,ans);
        return;
    }
    int x=q[u].first-1,y=q[u].second-1;
    
    a[x]+=3;
    dfs(u+1);
    a[x]-=3;
    
    a[y]+=3;
    dfs(u+1);
    a[y]-=3;
    
    a[x]+=1;
    a[y]+=1;
    dfs(u+1);
    a[x]-=1;
    a[y]-=1;
}


int main()
{
    int t;
    cin>>t;

    while (t--)
    {
        ans=10;
        cin>>n>>m;
        for(int i=0;i<n;i++) cin>>a[i];
        for(int i=0;i<m;i++)
        {
            int a,b;
            cin>>a>>b;
            q[i]={a,b};
        }
        dfs(0);
        cout<<ans<<endl;
    }
    return 0;
}

G题:

思路:

        先将价格排序,之后将遍历,优惠求前缀和,加上本金,如果大于当前价格,则答案赋值为当前优惠和与本金,遍历结束后,输出。

        注:因为要求前缀和,同样的,n最大为10的5次方m最大为10的9次方,bi最大也为10的9次方,所以需要long long 来存

代码:

        时间复杂度o(n)

#include<iostream>
#include <algorithm>
using namespace std;
typedef pair <long long ,long long > PII;
const int N=1e5+10;
PII e[N];
int main()
{
    int t;
    cin>>t;
    while (t--)
    {
        long long  n,m;
        cin>>n>>m;
        for(int i=0;i<n;i++)
        {
            int  a,b;
            cin>>a>>b;
            e[i]={a,b};
        }
        sort(e,e+n);
        long long ans=m;
        long long sum=m;
        for(int i=0;i<n;i++)
        {
            sum+=e[i].second;
            if(sum>=e[i].first) ans=sum;
        }
        cout<<ans<<endl;
    }
    return 0;
}

M题:

思路:

        当n能被整除时,答案为n/6;

        当n不能被整除时,答案为n/6*2;

代码:

        时间复杂度o(1)

#include <iostream>
using namespace std;
int main()
{
    int t;
    cin>>t;
    while (t--)
    {
        int ans=0;
        int n;
        cin>>n;
        if(n%6) ans=(n/6)*2;
        else ans=n/6;
         
        cout<<ans<<endl;
    }
    return 0;
}

  • 24
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值