2023CCPC题解

2023 年第五届河南省 CCPC 大学生程序设计竞赛

Problem A. 小水獭游河南

image-20240413201222597

思路:从第二个字母开始往后依次判断,先判断从这个字母往后是不是回文串,若是则输出HE结束当前循环,若不是判断当前字母之前出现过没,若出现过则输出NaN结束当前循环,若没出现过接着下一个字母判断,直到结束。

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

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        string a;
        cin>>a;
        //st用来存储字母出现的次数,用来判断是否多次出现该字母。
        int st[27],ji=0;
        //把st中的所有值赋为0。
        memset(st,0,sizeof(st));
        //若字符串中只有一个字母则不符合题意。
        if(a.size()==1)
        {
            cout<<"NaN"<<endl;
            continue;
        }
        //把第一个字母先加进去
        st[a[0]-'a']++;
        for(int i=1;i<a.size();i++)
        {   
            st[a[i]-'a']++;
            //判断回文
            for(int j=i,k=a.size()-1;j<a.size();j++,k--)
            {
                if(j>=k)
                {
                    cout<<"HE"<<endl;
                    //若成功则标记一下,直接结束当前循环
                    ji=1;
                    break;
                }
                if(a[j]!=a[k])
                {
                    break;
                }
            }
            //成功直接跳出
            if(ji==1)
            break;
            //重点:不能先判断是否重复,应该先判断是否为回文串
            if(st[a[i]-'a']>1)
            {
                cout<<"NaN"<<endl;
                break;
            }
        }
    };
    return 0;
}

Problem C. Toxel 与随机数生成器

image-20240413203404499

思路:由题意可知错误的生成器每次都生成同样的字符串,只需要把输入最短的字符串截取下来,用find查找一下若后面再次出现,则该生成器是错误的,若后面没出现过则是正确的。

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

int main()
{
    string s,b="";
    cin>>s;
    //截取字符串或者可以直接调用substr(pos, len)函数。
    for(int i=0;i<500;i++)
    b+=s[i];
    //判断是否存在过
    if(s.find(b,1001)==-1)
    {
        cout<<"Yes";
    }else
    {
        cout<<"No";
    }
    return 0;
}

Problem F. Art for Last

image-20240413203820541

题意:在序列中选k项,找到k项最小的两个差的绝对值和最大的两个差的绝对值的乘积的最小值。

思路:想要乘积的绝对值最小,肯定是找几个差值最小的区间,把这区间的最大值和最小值相乘,在比较这些区间的最小值即可

若是一个有序序列的区间,相邻元素的差值最小,在该区间的找出最大值即区间最后一个减去第一个;最小值(肯定是相邻元素的差值)则需要判断,若用双重循环判断可能超时(1e9*1e9=10亿乘10亿的数据量),就需要用滑动窗口来优化最差10亿的数据量。

通过滑动窗口得到每个区间的最小值,在用每个区间的最小值乘最大值依次比较取最小值。

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

int main()
{
    int n,k;
    cin>>n>>k;
    //a为输入数据,b为相邻数据的差值,c为每个区间的最小值,q为手写队列
    int a[n+10],b[n+10],c[n+10],q[n+10];
    for(int i=1;i<=n;i++)cin>>a[i];
    //把数据先排序
    sort(a+1,a+n+1);
    //算出每个相邻区间的差值
    for(int i=1;i<=n-1;i++)b[i]=a[i+1]-a[i];
    //滑动窗口(单调队列)
    int h=1,t=0,cnt=1;
    for(int i=1;i<=n-1;i++)
    {
        while(t>=h&&b[q[t]]>=b[i])t--;
        q[++t]=i;
        if(q[h]<i-k+2)h++;
        if(i>=k-1)c[cnt++]=b[q[h]];
    }
    //重点:单个元素最大值可能为1e9,相乘可能爆int,应该开long long
    long long res=1e18;
    for(int i=1;i<=n-k+1;i++)
    {
        res=min(res,(long long)c[i]*(a[i+k-1]-a[i]));
    }
    cout<<res;
    return 0;
}

Problem G. Toxel 与字符画

image-20240413205614138

思路:大模拟题,用三维数组,第三维的下标可以用来表示当前的数字。

单个元素数据量可能是1e18,存储的时候应该用longlong,先把等号前面的打印出来,在判断等号后面的是否符合题意,在判断的时候可能出现1e18*1e18可以用__int128数据类型__int128是128位,大概10^38范围(__int128是C20里的)。特别注意x=1时的特判,不然可能卡到1e18次循环即100亿亿次循环会超时。

#include <bits/stdc++.h>
using namespace std;
//打表
char big[10][10][9] = {
    {
        "........",
        "........",
        ".0000000",
        ".0.....0",
        ".0.....0",
        ".0.....0",
        ".0.....0",
        ".0.....0",
        ".0000000",
        "........",
    },
    {
        "........",
        "........",
        ".......1",
        ".......1",
        ".......1",
        ".......1",
        ".......1",
        ".......1",
        ".......1",
        "........",
    },
    {
        "........",
        "........",
        ".2222222",
        ".......2",
        ".......2",
        ".2222222",
        ".2......",
        ".2......",
        ".2222222",
        "........",
    },
    {
        "........",
        "........",
        ".3333333",
        ".......3",
        ".......3",
        ".3333333",
        ".......3",
        ".......3",
        ".3333333",
        "........",
    },
    {
        "........",
        "........",
        ".4.....4",
        ".4.....4",
        ".4.....4",
        ".4444444",
        ".......4",
        ".......4",
        ".......4",
        "........",
    },
    {
        "........",
        "........",
        ".5555555",
        ".5......",
        ".5......",
        ".5555555",
        ".......5",
        ".......5",
        ".5555555",
        "........",
    },
    {
        "........",
        "........",
        ".6666666",
        ".6......",
        ".6......",
        ".6666666",
        ".6.....6",
        ".6.....6",
        ".6666666",
        "........",
    },
    {
        "........",
        "........",
        ".7777777",
        ".......7",
        ".......7",
        ".......7",
        ".......7",
        ".......7",
        ".......7",
        "........",
    },
    {
        "........",
        "........",
        ".8888888",
        ".8.....8",
        ".8.....8",
        ".8888888",
        ".8.....8",
        ".8.....8",
        ".8888888",
        "........",
    },
    {
        "........",
        "........",
        ".9999999",
        ".9.....9",
        ".9.....9",
        ".9999999",
        ".......9",
        ".......9",
        ".9999999",
        "........",
    },
};

char sml[10][10][7] = {
    {
        "......",
        ".00000",
        ".0...0",
        ".0...0",
        ".0...0",
        ".00000",
        "......",
        "......",
        "......",
        "......",
    },
    {
        "......",
        ".....1",
        ".....1",
        ".....1",
        ".....1",
        ".....1",
        "......",
        "......",
        "......",
        "......",
    },
    {
        "......",
        ".22222",
        ".....2",
        ".22222",
        ".2....",
        ".22222",
        "......",
        "......",
        "......",
        "......",
    },
    {
        "......",
        ".33333",
        ".....3",
        ".33333",
        ".....3",
        ".33333",
        "......",
        "......",
        "......",
        "......",
    },
    {
        "......",
        ".4...4",
        ".4...4",
        ".44444",
        ".....4",
        ".....4",
        "......",
        "......",
        "......",
        "......",
    },
    {
        "......",
        ".55555",
        ".5....",
        ".55555",
        ".....5",
        ".55555",
        "......",
        "......",
        "......",
        "......",
    },
    {
        "......",
        ".66666",
        ".6....",
        ".66666",
        ".6...6",
        ".66666",
        "......",
        "......",
        "......",
        "......",
    },
    {
        "......",
        ".77777",
        ".....7",
        ".....7",
        ".....7",
        ".....7",
        "......",
        "......",
        "......",
        "......",
    },
    {
        "......",
        ".88888",
        ".8...8",
        ".88888",
        ".8...8",
        ".88888",
        "......",
        "......",
        "......",
        "......",
    },
    {
        "......",
        ".99999",
        ".9...9",
        ".99999",
        ".....9",
        ".99999",
        "......",
        "......",
        "......",
        "......",
    },

};

char inf[10][25] = {
    "........................",
    "........................",
    ".IIIIIII.N.....N.FFFFFFF",
    "....I....NN....N.F......",
    "....I....N.N...N.F......",
    "....I....N..N..N.FFFFFFF",
    "....I....N...N.N.F......",
    "....I....N....NN.F......",
    ".IIIIIII.N.....N.F......",
    "........................",
};

char den[10][9]{
    "........",
    "........",
    "........",
    "........",
    ".=======",
    "........",
    ".=======",
    "........",
    "........",
    "........",
};

int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        //用vector来存储每个数字的值
        vector<int> a,b,c;
        //定义一个字符串来存储结果
        string m[50];
        long long x,y;
        scanf("%lld^{%lld}",&x,&y);
        long long tx,ty;
        tx=x,ty=y;
        //把每一个数拆出来,注意拆除了是倒序
        while(tx)
        {
            a.push_back(tx%10);
            tx/=10;
        };
        while(ty)
        {
            b.push_back(ty%10);
            ty/=10;
        };
        //把数组内的数反转一下
        reverse(a.begin(),a.end());
        reverse(b.begin(),b.end());
     	//打印
        for(int i=0;i<10;i++)
        {
            //打印x
            for(auto k:a)
            m[i]+=big[k][i];
            //打印y
            for(auto k:b)
            m[i]+=sml[k][i];
            //打印等号
            m[i]+=den[i];
        }
        __int128 ans=1;
        //定义一个变量要来标记ans是否越界
        int ji=0;
        //重点:需要特判
        if(x!=1)
        {
         for(long long i=1;i<=y;i++)
        {
            ans*=x;
            if(ans>1e18)
            {
                ji=1;
                break;
            }
        }   
        }
        //若越界打印inf,不越界打印ans的值
        if(ji)
        {
            for(int i=0;i<10;i++)
            m[i]+=inf[i];
        }else
        {
            while(ans)
            {
                c.push_back(ans%10);
                ans/=10;
            }
            reverse(c.begin(),c.end());
            for(int i=0;i<10;i++)
            {
                for(auto k:c)
                m[i]+=big[k][i];
            }
        }
        //打印最后一列.
        for(int i=0;i<10;i++)m[i]+=".";
        //输出结果
        for(int i=0;i<10;i++)cout<<m[i]<<endl;
    };
    return 0;
}

Problem H. Travel Begins

image-20240413210929307

题意:{ai}的和为n。给你一个n和k个ai,通过给的四舍五入函数算出最大值和最小值。

思路:可知只要是大于等于0.5的的就可以看作1,只要尽可能的把n拆成尽可能多的0.5就能得到最大值。小于0.5的可以看作0把n拆乘尽可能接近0.5但小于0.5的值即可。

求最大值:一个n可以拆成2n个0.5,若2n个0.5>k,即只能拆出k-1个0.5,剩下的数给k的最后一个值(因为k个ai的和要为n),若2n<=k就可以把n都拆成0.5,把0.5都变成1在求剩下数的整数部分和小数部分即可。

求最小值:把一个n拆成尽可能多的接近0.5的数,需要判断的是如果2n=k的话,要拿出一个0.5,把剩下的0.5减去的值加到这上面来。

#include <bits/stdc++.h>
using namespace std;
//判断小数是否大于0.5
int check(double x)
{
    return x-int(x)>=0.5?1:0;
}
int main()
{
    int t;
    cin>>t;
    while(t--)
    {
        //大于一个尽可能小的数使得0.5-t接近0.5
        double t=0.000000000001;
        int n,k,num,ma,mi;
        cin>>n>>k;
        //判断最多可以分出多少个0.5
        if(2*n>k)
        num=k-1;
        else
        num=2*n;
        //num即为1的数量再加上剩下的整数和小数。
        ma=num+int(n-0.5*num)+check(n-0.5*num);
        //判断是否要提出一个0.5好让其他的0.5小于0.5
        if(2*n==k)
        num=2*n-1;
        //小于0.5的值为0即num为0,只须计算剩下的整数和小数即可。
        mi=int(n-(0.5-t)*num)+check(n-(0.5-t)*num);
        cout<<mi<<" "<<ma<<endl;
    }
    return 0;
}
posted @ 2024-04-13 19:39  cker  阅读(647)  评论(0)    收藏  举报