题解 | CDTU宜宾校区第一届大学生程序设计竞赛题解

开启修仙之旅

https://ac.nowcoder.com/acm/contest/35455/A

这次比赛题目还是具有一定思维性和技巧性也比较强,大家不要灰心,加油!

部分同学对于时间的规划以及做题决策有较大的提升空间。

第一题 入门修仙

#include <stdio.h>

int main(){
    printf("CDTU YYDS 1913~2022!");
    return 0;
}

第二题 进制转换

答案1042,具体计算方法不多说,也可以用计算器算

第三题 逃命

答案6 S->J->B->A,贪心策略,不多说

第四题 最长连续不下降的温度

题目很简单,有特殊点要处理而已

#include <iostream>
#include <cstring>
using namespace std;

int main(){
    long long n;
    cin>>n;
    if(n==1){
        cout<<1;
        return 0;
    }
    int a[n];
    for(int i=0;i<n;i++) cin>>a[i];
    int dp[n];
    memset(dp,0,sizeof(dp));
    dp[0]=1;
    int max=-0x3f3f3f;
    for(long long i=1;i<n;i++){
        if(a[i]>=a[i-1]){
            dp[i]=dp[i-1]+1;
            if(dp[i]>max)max=dp[i];
        }
        else dp[i]=1;

    }
    cout<<max;
}

第五题 Ultraman

纯属找规律

#include<bits/stdc++.h>
using namespace std;
int main()
{
    unsigned long long n,p,k;
    cin>>n>>p>>k;
    cout<<(p*k)%n;
    return 0;
}

第六题 来自霍格沃兹的骰子

这个题有个坑,就是一次都不抽,拿就不变啊!

这里来说一下这道题的思想

假设初始值为a[i],我们要抽k次,每次一定要是最大值

那么我们可以想一想,如果k的值小于a[i]呢?那取k次最大后,最后的数会不会就是6-k,如果大于a[i]的话,那我们肯定也会抽k次,但是其中有一次肯定是a[i],所以这个数一定会是6-k+1,所以最后的结果智慧与 a[i]和k的大小差值有关,而和a[i]本身无关。

#include <iostream>
using namespace std;
int a[6],k;
int main(){
    for(int i=0;i<6;i++)cin>>a[i];
    cin>>k;
    if(k)
    for(int i=0;i<6;i++)cout<<6-k+(a[i]>6-k?0:1)<<(i==5?"":" ");
    else for(int i =0;i<6;i++)cout<<a[i]<<(i==5?"":" ");
    return 0;
}

第七题 原神出金

递归版

#include <iostream>
using namespace std;
int get(int k){
    if(k==1)return 1;
    return k*get(k-1)%328;
}
int main(){
    int k;
    cin>>k;
    cout<<get(k)%328;
}

循环版:

#include <iostream>
using namespace std;

int main()
{
    long long n;
    cin>>n;
    int sum=1;
    for (int i = 1; i <= n; ++i) {
        sum*=i;
        sum%=328;
    }
    cout<<sum;
}

第八题 自动收割机

用数组可以过70%的数据,全过得用哈希表

#include <iostream>
#include <map>
using namespace std;
map<string,long long> mp;
int main(){
        string a;
        while (cin >> a) {
            mp[a] += 1;
            if (getchar() == 10)break;
        }
        int n;
        cin >> n;
        for (int i = 0; i < n; i++) {
            string a;
            cin >> a;
            cout << mp[a] << endl;
        }
}

第九题 矩阵乘法

考察循环嵌套

#include <bits/stdc++.h>
#define mm(a) memset(a,0,sizeof(a))
using namespace std;

int main()
{
        int n, m, n1, m1;
        cin >> n >> m;
        int a[n+1][m+1];
        mm(a);
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; ++j) {
                cin >> a[i][j];
            }
        cin >> n1 >> m1;
        int b[n1 + 10][m1 + 10];
        int c[n+10][m1+10];
        mm(b);
        mm(c);
        for (int i = 1; i <= n1; i++)
            for (int j = 1; j <= m1; ++j) {
                cin >> b[i][j];
            }
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m1; ++j) {
                for (int k = 1; k <= m1; k++) {
                    c[j][i] += a[i][k] * b[k][j];
                }
            }
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m1; ++j) {
                cout << c[i][j] << " ";
            }
            cout << endl;
        }
    
    return 0;
}

第十题 不定的斐波那契

算几组数据就能发现规律,奇数的值是-1,偶数的值是1,然后字符串处理即可,这里用的位运算判断奇偶性,位运算可以看我另外的文章

#include <iostream>
using namespace std;

int main(){
    string s;
    cin>>s;
    cout<<(((s[s.length()-1]-'0')&1)?-1:1);
    return 0;
}

第十一题 素数三元组

#include <iostream>
using namespace std;

const int N = 1010;
int f[N],cnt,res;

int is_prime(int n)
{
    if(n < 2) return 0;
    for(int i=2;i<=n/i;i++)
        if(n % i == 0) return 0;
    return 1;
}

int main()
{
    int m,n;
    cin >> m >> n;
    if(n<m){
        cout<<-1;
        return 0;
    }
    for(int i=m;i<=n;i++)
    {
        if(is_prime(i)) f[cnt ++ ] = i;
    }
    for(int i=0;i<cnt;i++)
    {
        for(int j=i + 1;j<cnt;j++)
        {
            for(int k=j + 1;k<cnt;k++)
            {
                if(is_prime(f[i] * f[j] + f[k])
                   &&is_prime(f[j] * f[k] + f[i])
                   &&is_prime(f[k] * f[i] + f[j]))
                    res ++;
            }
        }
    }
    cout << res;
    return 0;
}

第十二题 Three Bei

中等题的第一题,二维前缀和,本来打算卡时间,但想想算了

#include <iostream>
using namespace std;
int arr[1000];
int main(){
    string s;
    cin>>s;
    for(int i=1;i<=s.length();i++){
        if(isdigit(s[i-1]))
        arr[i]=arr[i-1]+s[i-1]-'0';
        else{
            cout<<"F";
            return 0;
        }

    }
    int ans=0;
    for(int i=1;i<=s.length();i++){
        for (int j = i; j <= s.length(); ++j) {
            if((arr[j]-arr[i-1])%3)continue;
            ans++;
        }
    }
    cout<<"T\n"<<ans;
    return 0;
}

如果最优解的话可以参考 蓝桥杯原题 - k倍区间,题目虽然不同,但大意相同

第十三题 重复数字

循环嵌套绝对爆

用循环嵌套绝对会爆,由于位运算:异或 的一些性质

0^A=A

A^A=0

所以,我们可以构造一个1~n的连续数组,和原数组相异或,最后留下来的那个就是重复的数字

#include <bits/stdc++.h>

using namespace std;

int main()
{
    long long T;
    cin>>T;
    while(T--){
        long long x;
        long long p=0;
        long long len=0;
        while(true){
            scanf("%d",&x);
            char c= getchar();
            p^=x^=len++;
            if(c==10)break;

        }
        printf("%03lld\n",p);
    }
    return 0;
}

第十四题 子串和

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
string s;
long long int i,j,w[100001],ennu[100001],ans=0,ls;

int main()
{
    int indexx[27];
    cin>>s;
    ls=s.length();
    for(i=0;i<=27;i++)
    {
        indexx[i]=0;
    }
    for(i=ls;i>0;i--)
    {
        //s[i+1]=s[i];
        ennu[i]=s[i-1]-'`';
    }
    for(i=1;i<=ls;i++)
    {
        w[i]=i*(ls-i+1);
    }
    for(i=1;i<=ls;i++)
    {
        if(!indexx[ennu[i]])
        {
            ans+=w[i];
        }
        else
        {
            ans+=w[i]-((w[i]/i)*indexx[ennu[i]]);
        }
        indexx[ennu[i]]=i;
    }
    cout<<ans;
    return 0;
}

第十五题 找到所有小辈

可以用深搜,本题有GPLT L2题目改编,深搜广搜的方法可以百度,这里给出Dj算法题解

#include <iostream>
#include <set>
#include <cstring>
#include <vector>
#define INF 0x3f3f3f
using namespace std;
set<pair<int,int>,less<pair<int,int> > >s;
typedef struct edge{
    int to,w,next;
}E[1000000];
E e;
int head[1000000];
long long _size=0;
void add(int u,int v,int w){
    e[_size]={v,w,head[u]};
    head[u]=_size++;
}
bool book[1000000];
int dist[1000000];

void init(){
    memset(head,-1,sizeof(head));
    memset(dist,INF,sizeof(dist));
}
int main(){
    int max=-0x3f3f3f;
    init();
    int n;
    cin>>n;
    int u=1;
    int pk=0;
    while(u<=n){
        int v;
        cin>>v;
        if(v==-1)v=0,pk++;
        add(v,u,1);
        u++;
    }
    dist[0]=0;
    s.insert({0,0});
    for(int i=0;i<n;i++){
        int u=s.begin()->second;
        book[u]=true;
        s.erase(*s.begin());
        for(int j=head[u];j+1;j=e[j].next){
            int v=e[j].to;
            if(!book[v]&&dist[v]>dist[u]+e[j].w){
                s.erase({dist[v],v});
                dist[v]=dist[u]+e[j].w;
                s.insert({dist[v],v});
                if(max<dist[v]) max=dist[v];
            }
        }
    }
    vector<int> q;
    for(int i=0;i<=n;i++){
        if(dist[i]==max)q.push_back(i);
    }
    if (pk>1)max--;
    cout<<max<<endl;
    for(int i=0;i<q.size()-1;i++) cout<<q[i]<<" ";
    cout<<q[q.size()-1];
}

第十六题 对峙

GPLT L2题目改编,考察知识点:并查集

#include <bits/stdc++.h>
using namespace std;
int a[101],mp[101][101];
void init(){
    for (int i = 0; i < 101; ++i) {
        a[i]=i;
    }
}
int find(int x){
    if(a[x]==x) return x;
    return a[x]=find(a[x]);
}

void merrge(int x,int y){
    if(find(x)==find(y))return ;
    a[find(x)]= find(y);
}

int main(){
    init();
    int n,m,k;
    cin>>n>>m>>k;
    for (int i = 0; i < m; ++i) {
        int u,v,w;
        cin>>u>>v>>w;
        if(w==1)merrge(u,v);
        else mp[u][v]=mp[v][u]=-1;
    }
    for (int i = 0; i < k; ++i) {
        int u,v;
        cin>>u>>v;
        bool f1= find(u)== find(v);
        bool f2= mp[u][v];
        if(f1&&f2) cout<<"OK but..."<<endl;
        else if(f1) cout<<"No problem"<<endl;
        else if(f2) cout<<"No way"<<endl;
        else cout<<"OK"<<endl;
    }
}

第十七题 CBT的层序遍历

#include <iostream>
using namespace std;
int N;//树中结点个数

int tree[35];
void hfind(int i=1){
    if(i>N) return;
    hfind(i<<1);
    hfind((i<<1)+1);
    cin>>tree[i];
}
int main(){
    cin>>N;
    hfind();
    for(int i=1;i<N;i++)
        cout<<tree[i]<<" ";
    cout<<tree[N];
    return 0;
}

第十八题 拔网线

图的最小生成树模板题,这里是Kruskal算法解析

#include <iostream>
#include <algorithm>
#include <vector>
#define v(p) vector<p>
#define add(u,v,w) e[e_size++]={u,v,w}//加边
using namespace std;
/*
边集数组存图
*/
typedef struct edge{
    int u,v,w;
    bool operator<(const edge &a)const {
        return w<a.w;
    }
}E[10001];
E e;
int e_size;//当前已存储边长
//加边的话直接用define,一句话没必要写函数,节省空间
bool cmp(struct edge a,struct edge b){
    return a.w<b.w;
}

/*
    并查集代码
*/
int a[10001];//并查集数组
//找祖宗,并把祖上几代都变成和自己同样的祖一代
int find(int x){
    if(a[x]==x)return x;
    return a[x]=find(a[x]);
}
//集合的合并,让x认y为爹,如果两个集合在同一个集合内,返回false,否则返回true
bool merrge(int x,int y){
    int in_x=find(x);
    int in_y=find(y);
    if(in_x==in_y)return false;
    a[in_x]=in_y;
    return true;
}
//初始化函数
void init(){
    //初始化代码
    //初始化并查集
    for(int i=0;i<10001;i++)a[i]=i;
    return;
}

int main(){
    init();//根据要求初始化
    int n,m,sum=0;
    cin>>n>>m;
    for(int i=0;i<m;i++){
        int u,v,w;
        cin>>u>>v>>w;
        add(u,v,w);
        sum+=w;
    }
    int cnt=0;//记边数
    sort(e,e+m);
    for(int i=0;i<m;i++){
        if(merrge(e[i].u,e[i].v)){
            sum-=e[i].w;
            if(++cnt==n-1)break;
        }
    }
    cout<<sum;
    return 0;
}

第十九题 超人小E

考察知识点:Dj算法 扩展欧几里得求逆元

#include <iostream>
#include <cstring>
#include <set>
#define INF 0x3f3f3f3f
using namespace std;
int cs=10;
/**
 *扩展欧几里得算法
 * @param a 数a
 * @param b 数b
 * @param x 代入方程中的x
 * @param y 代入方程中的y
 * @return a和b的最大公因数以及 x和y的解
 */
long long exgcd(int a,int b,int &x,int &y){
    if (b==0)   return x=1,y=0,a;
    long long t=x;
    x=y;
    y=t-a/b*y;
    return exgcd(b,a%b,x,y);
}
/**
 * 快速幂算法
 * @param x 底数
 * @param y 质数
 * @param mod 取余数
 * @return 返回x的y次方与mod模运算的值
 */
long long qpow(long long x,long long y,long long mod){
    long long res=1;
    while(y){
        if(y&1)res=res*x%mod;
        x=x*x%mod;
        y>>=1;
    }
    return res;
}
/**存边结构体*/
typedef struct edge{
    int to,w,next;
}Edge[100001];
/**存边结构体数组*/
Edge e;
int _size;
/**结点存储数组*/
int head[100001];
/**
 * 加边函数
 * @param u 起点
 * @param v 终点
 * @param w 权值
 */
void add(int u,int v,int w){
    e[_size]={v,w,head[u]};
    head[u]=_size++;
}
/**
 * 迪杰斯特拉算法,单源最短路径
 * @param s 源点
 * @param dist 存储最短权值的数组
 * @param book 存储记事本(记录是否通过某个点松驰过)
 * @param dist_size 最短权值数组长度 请使用sizeof
 * @param len 数组大小
 */
void dj(int s,int dist[],bool book[],int dist_size,int len){
    memset(dist,INF,dist_size);
    memset(book,false,dist_size/4);
    dist[s]=0;
    book[s]=true;
    /**用min_heap存储离源点最近的点,first为权值,second为点的编号 */
    set<pair<int,int> >min_heap;
    min_heap.insert({0,s});
    for (int i = 0; i < len-1; ++i) {
        int u=min_heap.begin()->second;
        book[u]=true;
        min_heap.erase(*min_heap.begin());
        for (int j = head[u]; j+1 ; j=e[j].next) {
            int v=e[j].to;
            if (!book[v]&&dist[v]>dist[u]+e[j].w){
                min_heap.erase({dist[v],v});
                dist[v]=dist[u]+e[j].w;
                min_heap.insert({dist[v],v});
            }
        }
    }
}
/**计算距离*/
long long getInstence(int u,int v){
    int x,y;
    exgcd(u,v,x,y);
    return qpow(x,y,109110);
}
int main(){
    int n,m,cdtu,k;
    cin>>n>>m>>cdtu>>k;
    memset(head,-1,sizeof head);
    for(int i=0;i<m;i++){
        int u,v;
        cin>>u>>v;
        long long w=getInstence(u,v);
        add(u,v, w);
        add(v,u,w);
    }
    int dist[100001];
    bool book[100001];
    dj(cdtu,dist,book,sizeof(dist),n);
    for(int i=0;i<k;i++){
        int u,v;
        cin>>u>>v;
        int du=dist[u];
        int dv=dist[v];
        if (du>=INF) {
            du=0;
            if (cs)cs--;
            else{
                cout<<-1<<endl;
                continue;
            }
        }
        if (dv>=INF) {
            dv=0;
            if (cs)cs--;
            else{
                cout<<-1<<endl;
                continue;
            }
        }
        cout<<du+dv<<endl;
    }
}

第二十题 树的连通块

知识点:树形DP

#include <cstdio>
#include <vector>
#include <algorithm>
#include <cstring>
using namespace std;
const int maxn=1005;
int n,m,k,x,y,l,o,r;
int nxt[maxn*2],last[maxn],to[maxn*2],Size[maxn],f[maxn][maxn],tmp[maxn],a[maxn];
vector <int> son[maxn];
void add(int x,int y)
{
    nxt[++l]=last[x];
    last[x]=l;
    to[l]=y;
}
bool cmp(int x,int y)
{
    return Size[x]<Size[y];
}
void dp(int u,int fa)
{
    vector<int>().swap(son[u]);
    for (int x=last[u];x;x=nxt[x])
    {
        int v=to[x];
        if (v==fa)  continue;
        dp(v,u);
        son[u].push_back(v);
    }
    sort(son[u].begin(),son[u].end(),cmp);
    if (a[u]>=o)    f[u][1]=1;
    Size[u]=1;
    for (int i=0,N=son[u].size();i<N;++i)
    {
        int v=son[u][i];
        memset(tmp,0,sizeof(tmp));
        for (int x=1;x<=Size[u];++x)
            for (int y=1;y<=Size[v];++y)
                tmp[x+y]=max(tmp[x+y],f[u][x]+f[v][y]);
        Size[u]+=Size[v];
        for (int x=1;x<=Size[u];++x)
            f[u][x]=max(f[u][x],tmp[x]);
    }
}
int main()
{
    scanf("%d%d%d",&n,&m,&k);
    for (int i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);
        r=max(r,a[i]);
    }
    for (int i=1;i<n;++i)
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    l=0;
    while (l<r)
    {
        o=l+r+1>>1;
        memset(f,0,sizeof(f));
        dp(1,0);
        if (f[1][m]>=k) l=o;
        else    r=o-1;
    }
    printf("%d\n",l);
    return 0;
}
全部评论

相关推荐

头像
不愿透露姓名的神秘牛友
04-08 00:50
点赞 评论 收藏
转发
2 收藏 评论
分享
牛客网
牛客企业服务