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

文章讲述了如何使用DFS搜索算法解决字符串子序列问题,以及利用贪心思想处理题目中的满减优惠问题。还涉及到了前缀和技巧和双指针法在空间和时间复杂度优化中的应用。
摘要由CSDN通过智能技术生成

我包菜的牢底

A DFS搜索 

题干


最近,fried-chicken完全学明白了DFS搜索(如上图所示)!于是学弟向他请教DFS搜索,fried-chicken热心的进行了讲解:

所谓DFS搜索,就是给定一个字符串sss,问能否找到sss的一个子序列,使得该子序列的值为 DFS 或 dfs。

请你分别判断字符串sss中是否含有 DFS 子序列与 dfs 子序列。

5
6
dafasa
6
dDFfSs
6
sfdDSF
6
DFSDFS
3
dfs

0 1
1 1
0 0
1 0
0 1

题解:

只要按DFS/dfs的顺序去寻找有没有完整出现过即可

void solve(){
  int n;
  string s;
  cin>>n>>s;
  int f1=0,f2=0,f3=0;
  for(int i=0;i<n;i++){
    if(s[i]=='D') f1=1;
    if(s[i]=='F' and f1==1) f2=1;
    if(s[i]=='S' and f2==1) f3=1;
  }
  if(f3) cout<<1<<" ";
  else cout<<0<<" ";
 f1=0,f2=0,f3=0;
  for(int i=0;i<n;i++){
    if(s[i]=='d') f1=1;
    if(s[i]=='f' and f1==1) f2=1;
    if(s[i]=='s' and f2==1) f3=1;
  }
    if(f3) cout<<1<<" ";
  else cout<<0<<" ";
  cout<<endl;
}

M 牛客老粉才知道的秘密

签到

题干:


现在,在本次比赛的主页点击"排名",您就会看到本场比赛的榜单,可以看到,榜单中直接列出了本场比赛的所有题目。

现在,作为牛客老粉,炸鸡想用这道题给大家科普一下牛客以前榜单的愚蠢之处:

牛客以前的榜单并不是现在这样,而是至多同时只显示六道题目。同时榜单上还有"向左"按钮与"向右"按钮来切换显示的题目。以"向右"按钮为例,点击一次该按钮会显示接下来的六道题,特别的,如果接下来的六道题超出了总题数,则会将最后一题放到当前显示的最右侧。"向左"按钮同理。

现在,你需要回答,对于nnn道题的一场比赛,显示的六道题目中最左侧的题目一共有几种可能取值。

以下面共n=14道题的情况为例:

题解:

列几组数据之后发现两种情况:1.能被6整除,直接输出n/6 2.不能整除就输出2*(n/6)

void solve(){
  int n;
  cin>>n;
  if(n%6==0){
    cout<<n/6<<endl;
  }
  else{
    cout<<(2*(n/6))<<endl;
    return ;
  }
}

G why买外卖

题干:

题解:

可以用pair<int,int>存放每组输入,再按a[i].first升序排序,再将a[i].second进行一次前缀和,这样就可以保证金额达到a[i].first时可以用的满减券最大。处理完之后直接贪心,如果现有金钱m加上满减券的前缀和大于等于对应的外卖金额,就更新答案。

pII a[N];
void solve(){
    int n,m,x,y;
    cin>>n>>m;
    vector<int>sum(n+1,0);
    for(int i=1;i<=n;i++){
        cin>>a[i].x>>a[i].y;
    }
    sort(a+1,a+n+1);
    // for(int i=1;i<=n;i++) cout<<a[i].x<<" "<<a[i].y<<endl;
    for(int i=1;i<=n;i++){
        sum[i]=sum[i-1]+a[i].y;
    }
    int ans=m;
    for(int i=1;i<=n;i++){
        if(m+sum[i]>=a[i].x) ans=max(ans,m+sum[i]); 
    }
    cout<<ans<<endl;
}

C 按闹分配

题干:

题解:

依然是前缀和问题,先将该序列升序排列,用前缀和处理一下,所得结果就是对应的人办完事情的时刻,也是每个人的不满意度。用于插队后,鸡后面的每个人不满意度都会+tc(鸡处理事情的时间,总不满意度就是tc*(鸡后面的人数),因此就能得到鸡插队的位置 pos=max(0,n-m/tc)。

最后不要忘记加上本身处理事情的时间tc

void solve(){
  int n,q,tc;
  cin>>n>>q>>tc;
  int t[n+1];
  for(int i=1;i<=n;i++) cin>>t[i];
  sort(t+1,t+1+n);
  int ans[n+1];
  ans[0]=0;
  for(int i=1;i<=n;i++) ans[i]=ans[i-1]+t[i];
  while(q--){
    int m;
    cin>>m;
    int tmp=max(0ll,n-m/tc);
    cout<<ans[tmp]+tc<<endl;
  }
} 

E :本题又主要考察了贪心

题干:

题解:

起初以为是分类讨论,后来发现无法判断当前的队伍在后面会不会还有比赛,再看数据范围可以用dfs,直接dfs。

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N =1e6+10;
const int INF=0x3f3f3f3f;
typedef pair<int, int> pII;
#define x first
#define y second
typedef long long ll;
#define pb push_back
const int mod=1e9+7;
#define endl '\n'
const double eps=1e-5;
int ddx[] = {0,1,3};
int ddy[] = {3,1,0};
  int n,m;
int a[N];
pII b[N];
  int ans;
void dfs(int u){
  if(u>=m){
    int cnt=1;
    for(int i=2;i<=n;i++){
      if(a[i]>a[1]) cnt++;
    }
    ans=min(ans,cnt);
    return ;
  }
  for(int i=0;i<3;i++){
    a[b[u].x]+=ddx[i];
    a[b[u].y]+=ddy[i];
    dfs(u+1);
    a[b[u].x]-=ddx[i];
    a[b[u].y]-=ddy[i];
  }
}
void solve(){
  cin>>n>>m;
 ans=n;
  for(int i=1;i<=n;i++) cin>>a[i];
  for(int i=0;i<m;i++) cin>>b[i].x>>b[i].y;
  dfs(0);
  cout<<ans<<endl;
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int T= 1;
    cin>>T;
   for(int i=1;i<=T;i++){
      solve();
   }
    return 0;
}

L :要有光

题干:

 题解:

几何题,把三维的投影成2维即可,答案为3cw

void solve(){
   double c,d,w,h;
    cin>>c>>d>>h>>w;
    cout<<fixed<<setprecision(4)<<3*c*w*1.0<<endl;
}

B 关鸡

题干:

题解: 

看鸡的左右两侧有没有被火封死,如果上下两层火的横坐标距离小于等于1的话代表这一侧封死,因此我们考虑以下几种情况

1. 左有火f1

2.右侧被封死 f2

3.左侧被封死 f3

4.右侧被封死f4

5.有特殊点(2,0) f5

但总之ans不可能超过3,因为只要围住鸡周围3格子他就不能动了,根据上述几种情况进行分类讨论即可。在判断是否封死是可以采用双指针方法

#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N =1e6+10;
const int INF=0x3f3f3f3f;
typedef pair<int, int> pII;
#define x first
#define y second
typedef long long ll;
#define pb push_back
const int mod=1e9+7;
#define endl '\n'
const double eps=1e-5;
int ddx[] = {1,0,0,-1};
int ddy[] = {0,1,-1,0};
void solve(){
    vector<int>x1,x2;
    int n;
    int cnt=0;
    bool f1=0,f2=0,f3=0,f4=0,f5=0;//务必务必初始化
    cin>>n;
    for(int i=0;i<n;i++){
        int x,y;
        cin>>x>>y;
        if(x==1) x1.pb(y);
        else x2.pb(y);
        if(y<0) f1=1;
        else if(y>0)f2=1;
        if(x==1 and y==-1) cnt++;
        if(x==2 and y==0) cnt++,f5=1;
        if(x==1 and y==1) cnt++;
    }
    int ans=3-cnt;
    // cout<<ans<<endl;
    sort(x1.begin(),x1.end());
    sort(x2.begin(),x2.end());
    int j=0;
    for(int i=0;i<x1.size();i++){
        while(j+1<x2.size() and x1[i]+1>=x2[j+1]) j++;
        if(j>=0 and j<x2.size() and abs(x1[i]-x2[j])<=1){
            if(x1[i]<0) f3=1;
            else f4=1;
        }
    }
    //双指针找纵坐标不同但横坐标小于1的点
    //下面分类
    // cout<<f1<<" "<<f2<<" "<<f3<<" "<<f4<<" "<<f5<<endl;
    if(f3 and f4){
        cout<<0<<endl;
    }
    else if(f3 and !f4){
        if(f5) cout<<min(1ll,ans)<<endl;
        else{
            if(f2) cout<<min(1ll,ans)<<endl;
            else cout<<min(2ll,ans)<<endl;
        }
    }
    else if(!f3 and f4){
          if(f5) cout<<min(1ll,ans)<<endl;
          else{
            if(f1) cout<<min(1ll,ans)<<endl;
            else cout<<min(2ll,ans)<<endl;
          }
    }
    else{
        if(f1 and f2) cout<<min(2ll,ans)<<endl;
        else cout<<min(3ll,ans)<<endl;
    }
}
signed main(){
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int T= 1;
    cin>>T;
   for(int i=1;i<=T;i++){
      solve();
   }
    return 0;
}

I  It's bertrand paradox. Again!

题干:

判断作业

两人的方法不同就在于前者的x,y一经确定就不修改,因此可以知道前者的半径期望一定小于后者,因此我们只算出找到两者半径r的期望值,再进行判断即可。

这边算出二者期望值

#include<bits/stdc++.h>
using namespace std;
int main() {
 default_random_engine e;
uniform_int_distribution<int> u1(-99,99); // 左闭右闭区间
uniform_int_distribution<int> u2(1,100); 
    e.seed(time(0));
    int n=1e5;
    double r1=0;
    for(int i=1;i<=n;i++){
        int x=u1(e),y=u1(e);
        while(1){
            int r=u2(e);
            if(x+r>100 or x-r<-100 or y+r>100 or y-r<-100){
                continue;
            }
            else{
                r1+=r;
                break;
            }
        }
    }
    double r2=0;
    for(int i=1;i<=n;i++){
        while(1){
              int x=u1(e),y=u1(e);
        int r=u2(e);
            if(x+r>100 or x-r<-100 or y+r>100 or y-r<-100){
                continue;
            }
            else{
                r2+=r;
                break;
            }
        }
    }
    cout<<r1/n<<" "<<r2/n<<endl;
}

r1=17.3742        r2=25.5075

算出期望值之后我们选定一个判断值,这个值只要在上面两个期望值中间取即可,我取了20,代码如下

void solve(){
       double sum,n;
       cin>>n;
       for(int i=1;i<=n;i++){
        int x,y,r;
        cin>>x>>y>>r;
        sum+=r;
       }
       if(sum/n<20) cout<<"bit-noob";
       else cout<<"buaa-noob";
}

其他的之后再补

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值