SMU Winter 2024 div2 4th


The Fourth Week

集腋成裘,聚沙成塔。几秒钟虽然不长,却构成永恒长河中的伟大时代。——弗莱彻

一、前言

周一又是牛客,我们三个人各ac一道题,不过B题其实我不太会,所以ac的是c题,大家又一块讨论了很久F,最后发现没有特判,wa12次。特判这个事真的很难搞。
打了俩场CF,我就一个问题,这网站怎么这么不稳定,打到一半卡住之后死活登不上去,时间到了连网站都加载不出来,目前才八百分,打算剩下时间多打几场,冲个分。
周三排位,身体不太舒服打的我晕头转向的。希望下次状态好一点吧。
周五牛客,ac了俩道题,就没有然后了。


二、算法

1.dfs

<1>(牛客训练营6 C)

题解:
给定一个整数n,n次询问,输入一个数字k后,输出三个相加为k的各异的fbnq数,如若不存在输出-1。
先用一个数组存下fbnq数列,找出不大于k的最大数,存入数组b1,再重复此操作,能找到三个数即输出。b为记录
代码:

#include<bits/stdc++.h>
using namespace std;

int b;
long long int b1[10];
long long int fib[90]={1,2};
void fi()
{
    for (int i = 2; i < 90; i++)
        fib[i] = fib[i - 2] + fib[i - 1];
}
int big(long long int n)
{
    int k;
    for(k=0;k<90;k++)
        if (fib[k + 1]>n)return k;
    return 0;
}
void solu(long long int n, bool first)
{
    int k = big(n);
    long long int left = n - fib[k];
    if (first)
    {
        if (!left)
        {
            b1[b++]=n;
            return;
        }
        if (left>0)
            solu(left, false);
        b1[b++]=fib[k];
    }
    else
    {
        if (left>0)
            solu(left, false);
        b1[b++]=fib[k];
    }
}
int main()
{
    fi();
    int t;
    cin >> t;
    while (t--)
    {
        long long int n;
        cin >> n;
        b=0;
        solu(n, true);
        if(b==3)
        {
            for(int i=0;i<3;i++)cout<<b1[i]<<" ";
        }
        else if(b<3)
        {
            for(int i=0;i<b;i++)cout<<b1[i]<<" ";
            for(int i=0;i<3-b;i++)cout<<0<<" ";
        }
        else cout<<-1<<endl;
        cout << endl;
    }
    return 0;
}

2.线性DP

<1>(洛谷 B3637)

一个线性DP的板子,直接贴代码了。
代码:

#include <bits/stdc++.h>
using namespace std;

int main () {
    int n;
    cin >> n;
    long long int a[n];
    int fi[5005] = {1};                     //感觉明明跟这行代码用处是一样的
    for (int i = 0 ; i < n ; i++) {
        cin >> a[i];
    }
    for (int i = 0 ; i < n ; i++) {
        fi[i] = 1;                          //不太理解,不写这一行代码才四十分
        for (int j = 0; j < i ; j++) {
            if ( a[i] > a[j] ) fi[i] = max ( fi[j]+1 , fi[i] );
        }
    }
    int ans=0;
    for (int i = 0; i < n; i++) {
        ans = max (fi[i] , ans);
    }
    cout << ans;
}

<2>(洛谷 P1091)

其实跟上面一题差不多,但我愣是没做出来,别人的代码倒是一看就懂,自己又编写了一遍
题解:
题意要求在n个小朋友中抽取k个,使得剩余的小朋友形成一个先增后降的最大序列,输出k。用数组fi,ki分别储存最大上升子序列和最大下降子序列,最后在每个人的位置上比较一下,即可知道最大的情况k是多少。
代码:

#include <bits/stdc++.h>
using namespace std;

int main () {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    cout.tie(nullptr);
    int n;
    cin >> n;
    int a[n];
    int fi[n];
    int ki[n];
    for (int i = 0;i < n;i++) {
        cin >> a[i];
    }
    for (int i = 0;i < n; i++) {
        fi[i]=1;
        for (int j =0 ;j < i;j++){
            if (a[i] > a[j])fi[i] = max (fi[i],fi[j]+1);
        }
    }
    for (int i = n-1;i >= 0; i--) {
        ki[i]=1;
        for (int j = n-1 ;j > i;j--){
            if (a[i] > a[j])ki[i] = max (ki[i],ki[j]+1);
        }
    }
    int ans=0;
    for ( int i = 0; i < n; i++){
        ans=max (ans,fi[i]+ki[i]-1);
    }
    cout << n-ans;
    return 0;
}

3.Dijkstra算法

捋了好一会儿这个算法,好像是个贪心的演化,适用于多坐标不同路长求最短路径。求最短路径常见的三种方法是BFS,Dijkstra(迪杰斯特啦),Floyd(弗洛伊德)。
DIJKSTRA(G, s) {
UnReachSet = G.V
对于每一个点v设置 v.key = ∞
对于每一个点v设置 v.previous = ∅
对于源点s设置 s.key = 0
while UnReachSet ≠ ∅
v = EXTRACT-MIN(UnReachSet)
对于从v出发的每条边(v, u)
// 松弛边(v, u)
if u.key > v.key + weight(u, v)
u.key = v.key + weight(u, v)
u.previous = v
}
找了很久,但是吧但是,这个算法真的好难,以后再看看

4.其它

这里面的题共同点就是,明明代码都看得懂,没有什么技术难度,但是做的时候又确实没有想到会是这个地方有问题,根本不明白自己为什么会错。可能还是题做太少了?这类型题都不太遇到?

<1>(CF round927 C)

题解:
这题有一个思想,顺向思维longlong不够大,得逆向取余,见注释。
代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
    long long int t;
    cin>>t;
    while(t--){
        long long int n,m;
        cin>>n>>m;
        long long int a[n];
        long long int sum=1;
        long long int b[n];
        for(int i=0;i<n;i++) {
            cin>>a[i];                   //3%4 * 5%3 * 6*4=3*5*6 %4
        }                                //直接全部乘法会爆long long
        string s;
        cin>>s;                          //倒着求余数
        long long int l,r;
        long long int bs=0;
        for(int i=0;i<n-1;i++){
            if(s[i]=='L')bs++;
        }l=r=bs;
        int wz=n-1;
        b[wz]=a[bs]%m;
        for(int i=n-2;i>=0;i--){
            wz--;
            if(s[i]=='L'){
                l--;
                b[wz]=b[wz+1]*a[l]%m;
            }
            else if(s[i]=='R'){
                r++;
                b[wz]=b[wz+1]*a[r]%m;
            }
        }
        for(int i=0;i<n;i++){
            cout<<b[i]<<' ';
        }cout<<endl;
    }

<2>(CF Gym103660F)

这题其实是一样的问题,感觉做这些题的时候没有什么算法技巧,但又没有什么思路,怎么办呢,这是后来补题做出来的。
题解:
题意较复杂,给定k,n俩个整数,经过计算约分后相加得出的值。
使用过快速幂,不过这题会超,必须得用数学思想。
代码:

#include<bits/stdc++.h>
using namespace std;

int main() {
   ios::sync_with_stdio(false);
   cin.tie(nullptr);
   cout.tie(nullptr);
   int t;
   cin>>t;
   while(t--){
       long long int n,k;
       cin>>n>>k;
       long long int ans=(1+n)*n/2;
       while(k>=1&&n>0){
           ans=ans-(1+n/2)*(n/2)/2;
           k--;
           n=n/2;
       }
       cout<<ans<<endl;
   }
   return 0;
}

三、总结

牛客周赛好像打完了,寒假也快结束了,应该只有一俩场了,这周学的算法都挺懵的,有时间还得都回溯一遍,之前的题目也去看了一下,部分题目可以补题了,说明还是有进步的嘛。下周还是dijkstra和DP吧,这俩个点都好难啊,dijkstra可以引申一下别的最短路径求法。

  • 14
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值