2024年中国传媒大学程序设计大赛AB题解(同步赛)

A题 小苯的区间和疑惑题

        

        用双向dp,dp[i]表示以第i位为结尾,从开始带第i位的最大区间大小,dpreverse[i]表示以第i位开始,到结尾的最大区间和,那么包含第i位置的最大区间和为dp[i]+dpreverse[i]-a[i];

解决代码如下

#include<iostream>
using namespace std;
int main (){
    int n;
    cin>>n;
    long long dp[n+2];
    long long dpreverse[n+2];
    long long a[n+2];
    for(int i=1;i<=n;i++)
        cin>>a[i];
    dp[0]=0;
    dpreverse[n+1]=0;
    for(int i=1;i<=n;i++){
        if(dp[i-1]<=0)
            dp[i]=a[i];
        else
            dp[i]=dp[i-1]+a[i];
    }
     for(int i=n;i>=1;i--){
        if(dpreverse[i+1]<=0)
            dpreverse[i]=a[i];
        else
            dpreverse[i]=dpreverse[i+1]+a[i];
    }
    for(int i=1;i<=n;i++)
        cout<<dp[i]+dpreverse[i]-a[i]<<" ";
    return 0;
}

B题 小苯的三元组

     

这一道题有两种思路,当然都不是我想出来的,当时写这道题的时候想着按照质数筛法筛出来每一位合适的数,统计一个数的倍数在数组中的个数,再找一个数的因数在数组中的个数,最后相乘,但是思路模糊也想不出来好的解决办法,下面是大佬的代码和相应的两种思路

法一:这三个数字的关系为前者是后者的因数,即c%b==0,b%a==0;

先用哈希表统计各个数字出现的次数,没有的为0;

然后遍历第一位i,从1到N,N为极大数,然后遍历到第一个存在的数字,然后再寻找第二个数字j,第二个数字为了满足可b%a==0的性质只能够以i为公差递增,同时在j数字存在的情况下寻找k,同理,k也以j为公差递增,最后找到合适的k值,那么统计次数ans=nums[i]*nums[j]*nums[k];

最后输出ans,解决代码如下

#include<iostream>
#include<map>
#include<algorithm>
typedef long long ll;
const ll N=200000;
ll nums[N];
using namespace std;
int main (){
    ll n;
    cin>>n;
    for(int i=0;i<n;i++){
        ll x;
        cin>>x;
        nums[x]++;
    }
    ll ans=0;
    for(int i=1;i<=N;i++){
        if(nums[i]){
            for(int j=i;j<=N;j+=i){
                if(nums[j]){
                    for(int k=j;k<=N;k+=j){
                        if(nums[k])
                            ans+=(nums[i]*nums[j]*nums[k]);               
                        
                    }
                }
                
            }
            
        }
        
    }
    cout<<ans;
    return 0;
}

第二个思路:

第二个思路,遍历每一位,统计以每一位为中间值的话满足题意的个数;

先预处理,lcd[x]表示数组中以x为因数的个数,即已知x就可以知道lcd[x](即第三个位置的可能情况);nums[x]表示x在数组中的个数,不存在的话为0,这样可以便于统计x的因数在数组中的个数和

解决思路

#include<iostream>
#include<map>
#include<cmath>
#include<algorithm>
int lcd[1000000];
int d[1000000];
using namespace std;
int main (){ 
    int n;
    cin>>n;
    int a[n];
    for(int i=0;i<n;i++){
        int x;
        cin>>a[i];
        x=a[i];
        for(int i=1;i<=sqrt(x);i++){
            if(x%i!=0)
                continue;
            lcd[i]++;            
            if(x/i!=i) lcd[x/i]++;
        }
        d[x]++;
    }
    long long ans=0;
    for(int i=0;i<n;i++){
        int x;
        x=a[i];
        int c1=0,c2=0;
        c2=lcd[x];
        for(int j=1;j<=sqrt(x);j++){
            if(x%j!=0)    continue;
            c1+=d[j];
            if(x/j!=j)    c1+=d[x/j];
        }
        ans+=c1*c2;
    }
    cout<<ans;
    return 0;
}

C题 小红的 CUC

签到题

D题 小红的矩阵构造(一)

这一题也是不会写,看了大佬的思路,大有启发,接下来是三种思路

第一种思路,

仔细审题,观察规律,如果一个位置行数+列数>n的话,那么这个位置就是一,反之则为零

解决代码

#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
int a[10000],b[10000];
int s[1010];
int mp[1010][1010];
int main (){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=n;i++){
        cin>>b[i];
    }
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            mp[i][j]=a[i]+b[j]>n?1:0;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            cout<<mp[i][j];
            cout<<endl;
    }
    return 0;
}

第二种思路

        矩阵是由一条条列矩阵形成,如果将每一条列矩阵都正确处理,就能够得出正确答案,

首先列数列的形成顺序是从多到少的,即先生成含有n个数的列数列,再生成含有n-1一个数字的列数列,其中缺少生成的那个数来自于某一行只能有一个数字,也就是生成列数列,行数列是限制列数列生成的条件,比如案例 1,2,3       和 1,2,3;

先考虑数最多的列,即第三列,同时没有行来约束,那么第三列全部数字为1;

然后考虑数第二多的列,即第二列,这是需要加入行带来的限制条件,即让第一行(再生成列的过程中的第一个数字),在生成第二列,加入行数第二少的限制条件

#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
pair<int,int> a[10000],b[10000];
int s[1010];
int mp[1010][1010];
int main (){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i].first;
        a[i].second=i;
    }
     for(int i=1;i<=n;i++){
        cin>>b[i].first;
        b[i].second=i;
    }
    sort(a+1,a+n+1);
    sort(b+1,b+n+1);
    for(int i=1;i<=n;i++){
        int cnt=0;
        int x=b[n-i+1].second;
            for(int j=1;j<=n;j++){
                if(s[j])
                    continue;
                else{
                    mp[j][x]=1;
                    cnt++;
            }
        }
        if(cnt!=n-i+1){
            cout<<-1;
            return 0;
        }
        s[a[i].second]=1;
    }
    for(int i=1;i<=n;i++){
        for(int j=1 ; j<=n;j++)
            cout<<mp[i][j];
        cout<<endl;
        
    }
    return 0;
}

第三种思路

列的先后顺序为多的先填,然后每一行填的个数已经决定好了,列多的先填,行次数用完的话剩下的就为0,这个思路应该是最容易理解的,解决代码如下

#include<iostream>
#include<map>
#include<algorithm>
using namespace std;
int a[10000];
pair<int,int>b[10000];
int s[1010];
int mp[1010][1010];
bool cmp(pair<int,int> a,pair<int,int> b){
    return a.first>b.first;
}
int main (){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        cin>>a[i];
    }
    for(int i=1;i<=n;i++){
        cin>>b[i].first;
        b[i].second=i;
    }
    sort(b+1,b+1+n,cmp);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            if(a[i]){
                mp[i][b[j].second]=1;
                a[i]--;
            }
        }
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++)
            cout<<mp[i][j];
        cout<<endl;
    }
 
        return 0;
}

E题 小红的矩阵构造(二)

#include<iostream>
using namespace std;
int main (){
    int n,m,k;
    cin>>n>>m>>k;
    int c=(n-1)*(m-1);
    if(c<k){
        cout<<-1;
        return 0;
    }
    char ch[n+1][m+1];
    for(int i=1;i<=n;i++)
        ch[i][1]='1';
    for(int j=1;j<=m;j++)
        ch[1][j]='1';
    int cnt=0;
    for(int i=2;i<=n;i++){
        for(int j=2;j<=m;j++){
            if(cnt>=k){
               ch[i][j]='0';
                continue;
            }
            ch[i][j]='1';
            cnt++;
        }  
    }
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cout<<ch[i][j];
        }
        cout<<endl;
    }
    
    return 0;
}

观察规律,先命令第一行和第一列为1,接下来每多一个矩阵就相当于增加一个一

H题 小红的生物实验

这一题我的思路不能正常运行,不知道为什么,我认为消去细胞壁的条件即将每一行的第一个和最后一个删去,把每一列的第一个和最后一个删去,但是答案错误;

接下来是dfs的思路,即标记所有外部环境的区域,然后记录其接触的细胞壁的位置,最后遍历,将外部环境,接触的细胞壁输出为'.',将未被标记的‘*’正常输出

#include<iostream>
using namespace std;
int n,m;
char a[1010][1010];
int vis[1010][1010];
void dfs(int x,int y){
    if(x<1||x>n||y<1||y>m)
        return ;
    if(vis[x][y])
        return ;
    vis[x][y]=1;
    if(a[x][y]=='*'){
        a[x][y]='.';
        return ;
    }
    dfs(x+1,y);
    dfs(x-1,y);
    dfs(x,y+1);
    dfs(x,y-1);
}
int main(){
    cin>>n>>m;
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)
            cin>>a[i][j];
    }
    for(int i=1;i<=n;i++)
        dfs(i,1),dfs(i,m);
    for(int i=1;i<=m;i++)
        dfs(1,i),dfs(n,i);
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++)
            cout<<a[i][j];
        cout<<endl;
    }
    return 0;
}

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Zzcat.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值