HNUCM2020年春季ACM集训队热身赛-第8场题解

问题 A: 人民币转换

题目描述

考试题目和要点:
1、中文大写金额数字前应标明“人民币”字样。中文大写金额数字应用壹、贰、叁、肆、伍、陆、柒、捌、玖、拾、佰、仟、万、亿、元、角、分、零、整等字样填写。
2、中文大写金额数字到“元”为止的,在“元”之后,应写“整字,如¥ 532.00应写成“人民币伍佰叁拾贰元整”。在”角“和”分“后面不写”整字。
3、阿拉伯数字中间有“0”时,中文大写要写“零”字,阿拉伯数字中间连续有几个“0”时,中文大写金额中间只写一个“零”字,如¥6007.14,应写成“人民币陆仟零柒元壹角肆分“。

输入

多组输入。输入一个double数。

输出

输出人民币格式。

样例输入

151121.15

样例输出

人民币拾伍万壹仟壹佰贰拾壹元壹角伍分

提示

10.56 读作人民币拾元伍角陆分
30005007 读作 三千万五千零七
30025007 读作 三千零二万五千零七
30020507 读作 三千零二万零五百零七

模拟题
位数都是4位四位的 ,个十百千,可以将数字分成很多个4位数字处理
//题目本来就比较麻烦,用字符数组处理会多很多操作,所以就用了C++的String

#include
#define ll long long
using namespace std;
const int maxn=3e5+5;
const int inf=0x3f3f3f3f;
char s[maxn];
string d[7]={"","仟","佰","拾",""};
string dig[10]={"零","壹","贰","叁","肆","伍","陆","柒","捌","玖"};
int aa[100],la;
int main()
{
    while(cin>>s+1){//输入的字符从1开始
        int ls=strlen(s+1),la=0;
        int a=0,b=0;
        for(int i=1;i<=ls;++i){
            if(s[i]=='.'){
                for(int j=i+1;j<=ls;++j)//小数部分的值
                    b=b*10+s[j]-'0';
                ls=i-1;
                break;
            }
        }
        for(int i=1;i<=ls;++i)//整数部分的值
            a=a*10+s[i]-'0';
        int _=a?1:0;//标记整数部分是否有值
        while(a){//将a分成多个四位数
            aa[++la]=a%10000;
            a/=10000;
        }
        string ans="人民币";
        for(int k=la;k>=1;--k){
            if(aa[k]==0){//如果这部分没有数字
                continue;
            }
            if(k<la&&(aa[k+1]%10==0||aa[k]/1000==0)){//如果前4位最后一个数为0或者当前位第一个数为0,加一个“零”
                ans+=dig[0];
            }
            int j=1000,ju=0;//ju=0为初始,ju=1表示碰到了非0数,ju=2表示出现非0数接0的状态
            for(int i=1;i<=4;++i,j/=10){
                int f=(aa[k]/j)%10;
                if(f){
                    if(ju==2)ans+=dig[0];//非0数中间有0
                    if(f==1&&i==3)ans+="拾";//十位为1需要特判
                    else ans+=dig[f]+d[i];
                    ju=1;
                }else if(ju==1){
                    ju=2;
                }
            }
            if(k==2)ans+="万";
            else if(k==3)ans+="亿";
        }
        if(_)
            ans+="元";
        if(b/10)
            ans+=dig[b/10]+"角";
        if(b%10)
            ans+=dig[b%10]+"分";
        if(b==0)ans+="整";
        cout<<ans<<endl;
    }
    return 0;
}

问题 B: 火车进站

题目描述

给定一个正整数N代表火车数量,0

输入

有多组测试用例,每一组第一行输入一个正整数N(0

输出

输出以字典序从小到大排序的火车出站序列号,每个编号以空格隔开,每个输出序列换行,具体见样例。

样例输入

3
1 2 3

样例输出

1 2 3
1 3 2
2 1 3
2 3 1
3 2 1

栈模拟
不过因为N只有一位也没必要用栈了,用数字就可以模拟栈
比如用数字x表示一个栈 向栈中加入数字y:x=x*10+y,出栈操作就是y=x%10,x/10

深搜所有结果,将结果排序进行输出

#include 
#define ll long long
using namespace std;
const int maxn=1e6+5;
int a[15],b[15],ans[maxn],n,cnt;
void dfs(int sta,int j,int v){//sta表示栈中的数字 j遍历要入栈的火车,v表示结果
    if(sta==0&&j>n){
        int x=0;
        ans[++cnt]=v;
        return;
    }
    if(j<=n) dfs(sta*10+a[j],j+1,v);//进站
    if(sta) dfs(sta/10,j,v*10+sta%10);//出站
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    dfs(0,1,0);//dfs深搜所有结果
    sort(ans+1,ans+cnt+1);//排序
    for(int i=1;i<=cnt;++i){
        for(int j=n;j>=1;--j){//将数字分解成一位位的
            b[j]=ans[i]%10;
            ans[i]/=10;
        }
        for(int j=1;j<=n;++j){
            printf("%d%c",b[j],j==n?'\n':' ');
        }
    }
    return 0;
}

问题 C: DNA序列

题目描述

一个DNA序列由A/C/G/T四个字母的排列组合组成。G和C的比例(定义为GC-Ratio)是序列中G和C两个字母的总的出现次数除以总的字母数目(也就是序列长度)。在基因工程中,这个比例非常重要。因为高的GC-Ratio可能是基因的起始点。
给定一个很长的DNA序列,以及要求的最小子序列长度,研究人员经常会需要在其中找出GC-Ratio最高的子序列。

输入

输入一个字符串型基因序列和整型子串的长度。

输出

找出GC比例最高的子串,如果有多个输出第一个的子串。

样例输入

AACTGTGCACGACCTGA
5

样例输出

GCACG

提示

字符串长度<=100000

长度一定,G和C数量越多GC比例越大。问题就变成找一个长度为k的串G和C数量最多

代码1:暴力枚举每一个起点

#include 
#define ll long long
using namespace std;
const int maxn=1e6+5;
char s[maxn],ans[maxn];
int main()
{
    int k,now=0,st=0,ans=0;
    scanf("%s %d",s+1,&k);
    int ls=strlen(s+1);
    for(int i=1;i+k-1<=ls;++i){
        int num=0;
        for(int j=i;j<=i+k-1;++j){
            if(s[j]=='C'||s[j]=='G'){
                ++num;
            }
        }
        if(ans<num){
            ans=num;
            st=i;
        }
    }
    for(int i=st;i<=st+k-1;++i)putchar(s[i]);
    putchar('\n');
    return 0;
}

代码2:前缀和优化
同样是枚举起点,不过求区间G和C的数量使用前缀计算

#include 
#define ll long long
using namespace std;
const int maxn=1e6+5;
char s[maxn],ans[maxn];
int pre[maxn];
int main()
{
    int k,now=0,st=0,ans=0;
    scanf("%s %d",s+1,&k);
    int ls=strlen(s+1);
    memset(pre,0,sizeof(pre));
    for(int i=1;i<=ls;++i){
        pre[i]=pre[i-1];
        if(s[i]=='C'||s[i]=='G')pre[i]++;
    }
    for(int i=1;i+k-1<=ls;++i){
        int num=pre[i+k-1]-pre[i-1];
        if(ans<num){
            ans=num;
            st=i;
        }
    }
    for(int i=st;i<=st+k-1;++i)putchar(s[i]);
    putchar('\n');
    return 0;
}

代码3:尺取法

#include 
#define ll long long
using namespace std;
const int maxn=1e6+5;
char s[maxn],ans[maxn];
int main()
{
    int k,now=0,st,ans;
    scanf("%s %d",s+1,&k);
    int ls=strlen(s+1);
    for(int i=1;i<=k;++i)//处理出第一个长度为k的串的GC
        if(s[i]=='C'||s[i]=='G')++now;
    ans=now,st=1;
    for(int i=k+1;i<=ls;++i){//不断向右移,更新GC
        if(s[i]=='C'||s[i]=='G')++now;
        if(s[i-k]=='C'||s[i-k]=='G')--now;
        if(now>ans){
            ans=now;
            st=i-k+1;
        }
    }
    for(int i=st;i<=st+k-1;++i)putchar(s[i]);
    putchar('\n');
    return 0;
}

问题 D: 尼科彻斯定理

题目描述

验证尼科彻斯定理,即:任何一个整数m的立方都可以写成m个连续奇数之和。
例如:
1^3=1
2^3=3+5
3^3=7+9+11
4^3=13+15+17+19

输入

多组输入,输入一个整数。

输出

输出分解后的字符串。

样例输入

6

样例输出

31+33+35+37+39+41

从题面就可以看出规律
1^3=1 第1个奇数
2^3=3+5 第2,3个奇数
3^3=7+9+11 第4,5,6个奇数
4^3=13+15+17+19 第7,8,9,10个奇数

#include
#define ll long long
using namespace std; 
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int main()
{
    ll n;
    while(~scanf("%lld",&n)){
        ll st=(n-1)*n/2;//计算起点前面有几个奇数
        st=2*st+1;//计算起点值
        for(ll i=1;i<=n;++i,st+=2){//n个奇数累加
            printf("%lld%c",st,i==n?'\n':'+');
        }
    }
    return 0;
}

问题 E: 牛妹的蛋糕

题目描述

众所周知,牛妹非常喜欢吃蛋糕。
第一天牛妹吃掉蛋糕总数三分之一多一个,第二天又将剩下的蛋糕吃掉三分之一多一个,以后每天吃掉前一天剩下的三分之一多一个,到第n天准备吃的时候只剩下一个蛋糕。
牛妹想知道第一天开始吃的时候蛋糕一共有多少呢?

输入

输入n,0

输出

输出第一天蛋糕的数量。

样例输入

2
4

样例输出

3
10

第i天有a个蛋糕,那第i+1天有a*2/3-1
也就是说第i+1天有a个蛋糕那第i天有(a+1)*3/2个
逆向计算n次即可

#include
#define ll long long
using namespace std; 
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int main()
{
    int n;
    while(~scanf("%d",&n)){
        int ans=1;
        for(int i=1;i<n;++i){
            ans=3*(ans+1)/2;
        }
        printf("%d\n",ans);
    }
    return 0;
}

问题 F: 牛妹的礼物

题目描述

众所周知,牛妹有很多很多粉丝,粉丝送了很多很多礼物给牛妹,牛妹的礼物摆满了地板。
地板是N×M 的格子,每个格子有且只有一个礼物,牛妹已知每个礼物的体积。
地板的坐标是左上角(1,1) 右下角(N, M)。
牛妹只想要从屋子左上角走到右下角,每次走一步,每步只能向下走一步或者向右走一步或者向右下走一步。
每次走过一个格子,拿起(并且必须拿上)这个格子上的礼物。
牛妹想知道,她能走到最后拿起的所有礼物体积最小和是多少?

输入

两个正整数N和M,0

输出

所有礼物的体积最小和。

样例输入

2 3
1 2 3
2 3 4

样例输出

7

提示

(1,1)->(1,2)->(2,3)

#include
#define ll long long
using namespace std; 
const int maxn=1e5+5;
const int inf=0x3f3f3f3f;
int a[305][305],dp[305][305];
int main()
{
    int n,m;
    while(~scanf("%d %d",&n,&m)){
        for(int i=1;i<=n;++i){
            for(int j=1;j<=m;++j){
                scanf("%d",&a[i][j]);
            }
        }
        memset(dp,inf,sizeof(dp));
        dp[1][1]=a[1][1];
        for(int i=1;i<=n;++i){
            for(int j=1;j<=m;++j){
                if(i+1<=n){//更新下边的体积
                    dp[i+1][j]=min(dp[i+1][j],dp[i][j]+a[i+1][j]);
                }
                if(j+1<=m){//更新右边的体积
                    dp[i][j+1]=min(dp[i][j+1],dp[i][j]+a[i][j+1]);
                }
                if(i+1<=n&&j+1<=m){//更新右下的体积
                    dp[i+1][j+1]=min(dp[i+1][j+1],dp[i][j]+a[i+1][j+1]);
                }
            }
        }
        printf("%d\n",dp[n][m]);
    }
    return 0;
}

问题 G: 车站建造问题

题目描述

有108个村庄排在一条公路上,依次编号为0~108-1,相邻村庄距离为1,其中有n个村庄居住着牛牛,居住着牛牛的村庄从小到大依次为a0~an-1,其中保证a0=0。
现在需要建设车站,有两个要求必须被满足:
1、每个有牛牛居住的村庄必须修建车站。
2、相邻车站的距离必须为1或为某个质数。
现给出n和a数组,求需要建设车站的最小数量。

输入

输入数据包含一个数n和一个长度为n的数组a,每一个样例中第一个数字表示n,后面n个数字为数组a中的数据。数组a的下标为0~n-1,保证a数组递增,且a0=0。
1<=n<=1000,a数组中数值保证小于10^8,且ai严格单调递增。

输出

输出一个整数,表示答案,即需要建设车站的最小数量。

样例输入

3
0 7 11

样例输出

4

提示

在0,7,8,11处建造车站,差值分别为7,1,3,符合要求。

每两个相邻车站要质数
如果两个相邻车辆距离不为质数,那就要转换成最少的质数相加
这个其实是哥德巴赫猜想…
如果不知道确实很难受

如果一个非质数x为偶数或者这个数可以分成2+(x-2) (x-2)为质数,那就可以分成两个质素相加
否则最少分成三个质数相加

#include
#define ll long long
using namespace std; 
const int maxn=3e5+5;
const int inf=0x3f3f3f3f;
int a[maxn];
bool ju(int a){
    if(a==1)return true;
    for(int i=2;i*i<=a;++i){
        if(a%i==0)return false;
    }
    return true;
}
int main()
{
    int n;
    scanf("%d",&n);
    for(int i=1;i<=n;++i)scanf("%d",&a[i]);
    int ans=n;
    for(int i=1;i<n;++i){
        if(ju(a[i+1]-a[i])==false){
            if((a[i+1]-a[i])%2==0||ju(a[i+1]-a[i]-2)){
                ++ans;
            }else{
                ans+=2;
            }
        }
    }
    printf("%d\n",ans);
    return 0;
}

问题 H: DNA序列II

题目描述

牛牛又从生物科研工作者那里获得一个任务,这次牛牛需要帮助科研工作者从DNA序列s中找出最短没有出现在DNA序列s中的DNA片段的长度。
例如:s = AGGTCTA
序列中包含了所有长度为1的(‘A’,‘C’,‘G’,‘T’)片段,但是长度为2的没有全部包含,例如序列中不包含"AA",所以输出2。

输入

输入包括一个字符串s,字符串长度length(1 ≤ length ≤ 20000),其中只包含’A’,‘C’,‘G’,'T’这四种字符。

输出

输出一个正整数,即最短没有出现在DNA序列s中的DNA片段的长度。

样例输入 Copy

AGGTCTA

样例输出 Copy

2

枚举每一个可能的长度进行判断
看看每一个长度的字符串去重(set)后是数量足够。。。。

#include
#define ll long long
using namespace std; 
const int maxn=3e5+5;
const int inf=0x3f3f3f3f;
string s;
int pow_(int a,int b){//快速幂
    int ans=1;
    while(b){
        if(b&1)ans*=a;
        a*=a;
        b>>=1;
    }
    return ans;
}
int main()
{
    cin>>s;
    int ls=s.length();
    for(int len=1;len<=ls;++len){
        set<string> se;
        for(int i=0;i+len-1<=ls;++i){
            string x=s.substr(i,len);//字符串截取函数
            se.insert(x);
        }
        if(se.size()<pow_(4,len)){//长度为len的字符有4^len种组合
            printf("%d\n",len);
            break;
        }
    }
    return 0;
}

你可能感兴趣的:(HNUCM2020年春季ACM集训队热身赛-第8场题解)