2024牛客寒假算法基础集训营5

文章介绍了几个编程问题,涉及质数合数判断、字符串操作中的迷茫值计算、动态规划、排列构造、最短路径、数组操作优化、贪心策略以及背包问题等,展示了IT技术中基础算法的应用和解决思路。
摘要由CSDN通过智能技术生成

目录

A.mutsumi的质数合数

B.tomorin的字符串迷茫值

C.anon的私货

E.F.soyorin的数组操作(easy & hard)

G.H.sakiko的排列构造(easy & hard)

I.rikki的最短路

J.rikki的数组陡峭值

K.soyorin的通知

L.anon的星星

M.mutsumi的排列连通


A.mutsumi的质数合数

简单数学知识:1既不是质数也不是合数

void solve(){
   
    cin>>n;
    int ans=0;
    for(int i=1;i<=n;i++){
        int x; cin>>x;
        if(x==1) continue;
        ans++;
    }
    cout<<ans<<endl;
    return ;
}

B.tomorin的字符串迷茫值

性质dp

观察题目本质,由于不能连续删除所以留下带有指定字符的

"mygo","m_ygo","my_go","myg_o","m_y_go","m_yg_o","my_g_o","m_y_g_o"只有这些情况,所以在这样的情况下后面周围的随便选(周围的随便选其实就是斐波那契数列),然后乘积起来即可

void solve(){
    string s; cin>>s;
    n=s.size();
    vector<int> dp(n+1,1);
    dp[1]=2;
    for(int i=2;i<=n;i++) dp[i]=(dp[i-1]+dp[i-2])%mod;
    vector<string> t={"mygo","m_ygo","my_go","myg_o","m_y_go","m_yg_o","my_g_o","m_y_g_o"};
    int ans=0;
    for(int i=0;i<n;i++){
        for(auto&v:t){
            string c=s.substr(i,v.size());
            int ok=0;
            for(int j=0;j<v.size();j++){
                if(v[j]=='_') ok++;
                else if(v[j]==c[j]) ok++;
            }
            if(ok==v.size())(ans+=dp[i]*dp[n-i-ok]%mod)%=mod;
        }
    }
    cout<<ans<<endl;
   
    return ;
}

C.anon的私货

思维贪心,我们直接按照要求来排放最多的0即可排了之后对当前数减去后面的排了的0注意顶一个数和最后一个数都是可以扩充的

void solve(){
   
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    LL ans=0;
    a[0]=2e9;
    for(int i=1;i<=n;i++){
        int x=min(a[i-1]-1,a[i]-1);
        ans+=x;
        a[i]-=x;
    }
    ans+=max(0,a[n]-1);
    cout<<ans<<endl;
    return ;
}

E.F.soyorin的数组操作(easy & hard)

首先对于这种题目发现性质,可以明显得知如果是1或者是偶数是一定有解的,对于奇数我们如何操作呢,可以发现最后一个数是奇数是不可以操作的也就是我们的限高,然后我们发现操作的越多是越有利的,如果我们从前面开始的话不可知上限由于后面的数还会变化,但是我们从后面开始的话上限是可知的,同时可以保留每次的处理,所以我们倒着来每次都处理到上限位置即可,然后判断是否有解,对于求解操作次数我们发现如果是有解的话那么一定就是每一次操作都可以减少最大的一次所以我们的操作次数就是其中的最大差值

void solve(){
    res=0;
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=2;i<=n;i++){
        res=max(res,a[i-1]-a[i]);
        
    }
    if(n%2==0 || n==1){
        cout<<res<<endl;
        return ;
    }
    
    LL cnt=0;
    for(int i=n;i>=1;i--){
        a[i]+=cnt*i;
        if(i%2==0){
            LL t=max(0ll,(a[i+1]-a[i])/i);
            a[i]+=t*i;
            cnt+=t;
        }
    }
    bool ok =true;
    for(int i=2;i<=n;i++)
        if(a[i]<a[i-1]){
             ok=false;
        }
    cout<<(ok ? res : -1)<<endl;
    return ;
}

G.H.sakiko的排列构造(easy & hard)

规律发现和打表思考题

我们可以发现如果说是easy的范围我们可以使用匈牙利算法来直接解决问题,对于hard的问题我们要思考是不是具有性质基本上就是o(n)的解决题目,我们思考如果说1+x是质数那么这里面的数都是可以组合为质数的  2+x-1=3+x-2=...,假设 x+1+n也是质数那么整个题目就可以直接解决了,对于判断质数可以使用筛法也可以使用直接判断,所以我们发现(打表)一定是可以找到的

void intn(int n)
{
   for(int i=2;i<=n;i++){
       if(!st[i]) primes[cnt++]=i;
       for(int j=0;primes[j]<=n/i;j++){
           st[i*primes[j]]=true;
           if(i%primes[j]==0) break;
       }
   }
}

void solve(){
    cin>>n;
    intn(2*n+5);
    int r=n;
    for(int i=n;i>=1;i--){
        if(!st[i+r]){
            for(int j=i;j<=r;j++){
                ans[j]=i+r-j;
            }
            r=i-1;
        }
    }
    for(int i=1;i<=n;i++) cout<<ans[i]<<' ';
    cout<<endl;
    return ;
}

I.rikki的最短路

我们发现问题的核心是相对位置的确定以及是不是第一次就可以直接去A,搞清楚之后就很简单

void solve(){
   
    LL t,a,k; cin>>t>>a>>k;
    if(a<=k && a>=-k){// 看到了a  到a然后去t
        LL ans = abs(a)+abs(t-a);
        cout<<ans<<endl;
    }
    else{
        if(abs(a)>=abs(t)){
            if((a>=0 && t>=0) || (a<=0 && t<=0)){// 一侧
                 LL ans = abs(a)+abs(a-t);
                 cout<<ans<<endl;
            }
            else{// 不是同一侧
                LL ans = 3*abs(t) + 2*abs(a);
                cout<<ans<<endl;
            }
        }
        else{
            if((a>=0 && t>=0) || (a<=0 && t<=0)){// 一侧
                LL ans = abs(t);
                cout<<ans<<endl;
            }
            else{// 两侧
                LL ans = 3*abs(t) + 2*abs(a);
                cout<<ans<<endl;
            }
        }
    }
    return ;
}

J.rikki的数组陡峭值

贪心求解基本上我们可以知道如果第一个点确定之后我们直接围绕他开始贪心即可,而我们发现只要是按照区间相交的操作来维护直接就可以得到答案所以可以这样贪心

PII w[N];
void solve(){
   
    cin>>n;
    for(int i=1;i<=n;i++){
        int l,r; cin>>l>>r;
        w[i]={l,r};
    }
    int L=w[1].first,R=w[1].second;
    LL ans=0;
    for(int i=2;i<=n;i++){
        auto [l,r]=w[i];
        if(l>R){
            ans+=l-R;
            L=R=l;
        }
        else if(r<L){
            ans+=L-r;
            L=R=r;
        }
        else{
            L=max(L,l);
            R=min(R,r);
        }
    }
    cout<<ans<<endl;
    return ;
}

K.soyorin的通知

注意题目也就是第一个人一定是固定需要话这么多才能开始操作的,然后对于至多的意思就是小于他的也是可以操作的所以我们都要进行处理,然后对于1,p也是可以处理的搞清楚题目的整个意思后整个题就是一个完全背包了

LL w[M];
LL f[M];
void solve(){
    
   cin>>n>>p;
   for(int i=1;i<=n;i++) w[i]=1e18;
   for(int i=1;i<=n;i++){
       int x,y; cin>>x>>y;
       for(int j=1;j<=min(y,n);j++){
           w[j]=min(w[j],x);
       }
   }
   for(int i=0;i<=n;i++) f[i]=3e18;
     
   f[1]=p;
   w[1]=min(w[1],p);
   for(int i=1;i<=n;i++)
        for(int j=i+1;j<=n;j++){
            f[j]=min(f[j],f[j-i]+w[i]);// 多次更新 
        }       
    cout<<f[n]<<endl;
 
    return ;
}

L.anon的星星

暴力

我们可以发现数据范围很小直接暴力即可

void solve(){
    
    cin>>n>>m;
    for(int i=0;i<=n;i++){
        int ans=i-(n-i);
        if(ans==m){
            cout<<i<<' '<<n-i<<endl;
            return ;
        }
    }
    cout<<-1<<endl;
    return ;

M.mutsumi的排列连通

思考一下如果要不连通最多就是删除中间位置的两个数即可,也就是答案的上限就是2,

乐意思考到如果是1是无解的如果是 12 同时是排列整齐的也是无解

接着我们考虑一个的情况,

1.(在中间)同一个位置的两个数是一样的

2.\frac{ab}{ba}这样的a,或者是b都是一次

所以按照要求模拟即可

void solve(){
   
    cin>>n;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++) cin>>b[i];
    if(n==2){
        if(a[1]==b[1]){
            cout<<-1<<endl;
        }
        else{
            cout<<1<<endl;
        }
        return ;
    }
    if(n==1){
        cout<<-1<<endl;
        return ;
    }
    else{
        for(int i=2;i<n;i++){
            if(a[i]==b[i]){
                cout<<1<<endl;
                return ;
            }
        }
        for(int i=1;i<=n;i++){
            if(i+1<=n && a[i]==b[i+1]){
                cout<<1<<endl;
                return ;
            }
            if(i-1>=1 && a[i]==b[i-1]){
                cout<<1<<endl;
                return ;
            }
        }
        cout<<2<<endl;
    }
    return ;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值