2024春季期中测验-入门组补提报告

小可的疑惑(problem)

赛前:


n=k*b+c,使得b*c取max
在和相同的情况下,两数越接近乘积就越大。但是担心正好被整除导致数字不是最大的,直接写了暴力
根据数据范围,暴力可以得70分

赛后:

思路:


  n=k*b+c,要使b*c最大,k一定为1,此时n=b+c,所以要想让b*c最大,只需要让b和c在为整数的情况下,尽可能的接近就可以了。
  若n为复数,b一定不能等与n/2,否则答案为0.


ac代码:

#include<iostream>
using namespace std;
int n,k;
int main(){
    scanf("%d",&n);
    long long k=n/2+1;
    printf("%ld",(n%k)*k);
    return 0;
}


注意:

最后的乘积可能超过int范围,使用long long。

达达的考验(test)

赛前:


给定一个字符串,求里面的每一个数字+5后的和
字符串在1e5之内可以直接便利,遇到数字就加起来,最后直接输出
100
没看见前面的-1是第几个考验->0

赛后:

思路:


  便利整个字符串,遇到数字的时候就记录一下并加起来,最后输出就可以了。


ac代码:

#include<iostream>
#include<algorithm> 
using namespace std;
int n;
string s;
long long sum=0;
int main(){
    scanf("%d",&n);
    cin>>s;
    for(int i=0;i<s.size();i++){
        long long sum1=0;
        while(s[i]>='0'&&s[i]<='9'){
            sum1=sum1*10+(s[i++]-'0');
        }
        sum+=sum1+5;
    }
    printf("%ld",sum);
    return 0;
}


注意:

使用long long

小可赛马(horse)

赛前:

给定一个长度为n的计划表,其中i个数字需要出现a[i]次才可以出战。接下来输入一个m,查询m次在l到r的序列中有没有k个数可以出战。
l到r的区间数使用前缀和,但数据范围1e5,数组需要开1e10显然不行,放弃。时间原因直接使用暴力,有40%的数据在暴力范围之内。
40

赛后:

思路:


  解决区间求和问题,我们使用前缀和。n的范围是1e5,所以二维前缀和第一层开10^5肯定是没有问题,但是第二层如果开10^5的话空间肯定会超限。在一个10^5的数组中最多有478个数是可以出战的,像这样:1,2,2,3,3,3,4,4,4,4……478.所以二维前缀和数组只需要开到cnt[1e5][500]就可以了。
  然后再使用离散化,是每个数组对应一个值,例如:1 5 700 1000 ->对应下标 1 2 3 4.问题就解决了。
  如下使用:
 

 for(int i=1;i<=n;i++){
        scanf("%d",&x[i]);
        if(x[i]<=n) a[x[i]]++;
    }
    for(int i=1;i<=n;i++){
        if(a[i]>=i) b[++sum]=i;
    } 
    for(int i=1;i<=sum;i++){
        for(int j=1;j<=n;j++){
            cnt[j][i]=cnt[j-1][i]+(x[j]==b[i]);
        }
    }


 时间复杂度最高为5*10^7,不会超限。
ac代码:

#include<iostream>
#include<algorithm> 
using namespace std;
const int N=1e5+5;
int n;
int a[N],cnt[N][500],x[N],b[N],sum;
int m,l,r,k;
int f(int cnt,int k){
    if(k==1) return cnt;
    if(k==2) return cnt*(cnt-1)/2;
    if(k==3) return cnt*(cnt-1)*(cnt-2)/6;
    if(k==4) return cnt*(cnt-1)*(cnt-2)*(cnt-3)/24;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++){
        scanf("%d",&x[i]);
        if(x[i]<=n) a[x[i]]++;
    }
    for(int i=1;i<=n;i++){
        if(a[i]>=i) b[++sum]=i;
    } 
    for(int i=1;i<=sum;i++){
        for(int j=1;j<=n;j++){
            cnt[j][i]=cnt[j-1][i]+(x[j]==b[i]);
        }
    }
    scanf("%d",&m);
    while(m--){
        scanf("%d%d%d",&l,&r,&k);
        int cnt1=0;
        for(int i=1;i<=sum;i++){
            if(cnt[r][i]-cnt[l-1][i]>=b[i]){
                cnt1++;
            }
        }
        cout<<f(cnt1,k)<<"\n";
    }
    return 0;
}


注:

k的范围较小,所以可以直接枚举来做,这里使用的是C(m,n)=m!/(n!*(m-n)!);

括号专家(expert)

赛前:


给定一个n、a、b,一个长度为n的只包含“(”和“)”的字符串,每组匹配的括号有0,1,2三种状态,0状态的收益为0,1状态的收益为a,2状态的收益为b,相邻的不匹配的括号状态不能相同,求最大收益。
40%的数据()()()()⋯这样的,这种序列只有一种情况:一组a状态,一组为b状态,判断单复即可。
40

赛后:


思路:


  若[1,x]是一对匹配括号,那么它可以将字符串分为两个部分:[1,x]和[x+1,n];
  数组dp[l,r,p,q]表示点l为p状态,r为q状态。则有:
    [1,x]均为p状态
    那么[x+1]为!p状态需要枚举
    [n]则为p状态
    用动态规划如下使用:

 int ans=0;
        for(int i=0;i<=2;i++){
            if(i!=p){
                ans=max(ans,dfs(x[l]+1,r,i,q));
            }
        }
        dp[l][r][p][q]=ans+dfs(l,x[l],p,p);
        return dp[l][r][p][q];


    考虑一种特殊情况:当[1,x]是[1,n]的时候,那么它就没法把整个字符串分为两部分了
    此时,我们把字符串变成[2,n-1]继续使用以上方法    
    代码如下:
   

 if(p!=q) return 0;
        int ans=0;
        for(int i=0;i<=2;i++){
            for(int j=0;j<=2;j++){
                if(i!=p&&j!=q) ans=max(ans,dfs(l+1,r-1,i,j));
            }
        }
        dp[l][r][p][q]=ans+c[p];
        return dp[l][r][p][q];


    注意:要记得加上[1,x]的价值
    求[1,x]可以使用栈进行枚举:
  

  for(int i=0;i<s.size();i++){
        if(s[i]=='(') st.push(i);
        else{
            x[i+1]=st.top()+1;
            x[st.top()+1]=i+1;
            st.pop();
        }
    }


ac代码:
 

#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;
const int N=1e3+5;
int n,x[N];
int dp[N][N][3][3],c[3];
string s;
stack<int> st;
int dfs(int l,int r,int p,int q){
    if(l>=r) return 0;
    if(dp[l][r][p][q]!=0) return dp[l][r][p][q];
    if(x[l]==r){
        if(p!=q) return 0;
        int ans=0;
        for(int i=0;i<=2;i++){
            for(int j=0;j<=2;j++){
                if(i!=p&&j!=q) ans=max(ans,dfs(l+1,r-1,i,j));
            }
        }
        dp[l][r][p][q]=ans+c[p];
        return dp[l][r][p][q];
    }else{
        int ans=0;
        for(int i=0;i<=2;i++){
            if(i!=p){
                ans=max(ans,dfs(x[l]+1,r,i,q));
            }
        }
        dp[l][r][p][q]=ans+dfs(l,x[l],p,p);
        return dp[l][r][p][q];
    }
}
int main(){
    scanf("%d%d%d",&n,&c[1],&c[2]);
    cin>>s;
    for(int i=0;i<s.size();i++){
        if(s[i]=='(') st.push(i);
        else{
            x[i+1]=st.top()+1;
            x[st.top()+1]=i+1;
            st.pop();
        }
    }
    int ans=0;
    for(int i=0;i<=2;i++){
        for(int j=0;j<=2;j++){
            ans=max(ans,dfs(1,n,i,j));
        } 
    }
    printf("%d",ans);
    return 0;
}

做题顺序:

2->3->1->4
时间分配:前10分钟把每个题都看了一遍,第二道和第三道有一点思路,快速写完第二道,卡在第三道,共做了20分钟,发现时间不多了,写了第一道和第三道的暴力,第四题写了其中一种情况的代码。总用时40分钟。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值