ecnu18级程设实训第一次机考简易解题报告

Posted wengsy150943

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ecnu18级程设实训第一次机考简易解题报告相关的知识,希望对你有一定的参考价值。

 

愉快(并不)地考完了,看到LDY大佬的题解,决定学习一个,也来写一份解题报告。当然,这会是一个比较简易的版本,可能有所缺漏,敬请谅解。
此处是LDY大佬题解的传送门:
走过路过给个赞~
A. 恢复单词

单点时限: 2.0 sec

内存限制: 512 MB

学霸在背单词的时候发现单词的大小写混乱,而且可能参杂了0-9的数字和 %,+,&,#这四种特殊符号。请编写程序让单词恢复正常。要求处理后单词的首字母大写,且其余字母均为小写。

注意:单词虽然大小写混乱,但是字母间的顺序均正确。

例如:%sa+01t2urdAY,经过处理后为:Saturday

输入格式

混乱的单词s1s1的长度<=100。

输出格式

去掉数字和特殊字符后的单词,单词首字母大写,其余字母小写。
s1中的数字
s1中的其它字符

样例

input
%sa+01t2urdAY
output
Saturday
012
%+
input
book
output
Book

提示

若对应内容不存在,则不用输出任何信息。

 

第一题考的是字符串的基本操作,用STL的string类可以说是事半功倍,如果用字符数组实现,大概要注意一下结尾的 ‘ \0 ‘。注意单词的大小写判断。还有提示里“任何信息”包括空格。

技术图片
#include<bits/stdc++.h>//万能头文件
using namespace std;
int main(){
    string s1,word,num,other;
    int l,i;
    bool firLetter(false);//判断是否是第一个字母
    cin>>s1;
    l=s1.length();//获得s1的长度
    for(i=0;i<l;i++){
        if(s1[i]>=a&&s1[i]<=z||s1[i]>=A&&s1[i]<=Z){//如果是字母
            if(!firLetter&&s1[i]>=a)//是首字母且为小写
                s1[i]+=A-a;
            else if(firLetter&&s1[i]<a)//不说首字母且为大写
                s1[i]+=a-A;
            word+=s1[i];
            firLetter=true;    //接下去的都不是第一个字母    
        }
        else if(s1[i]>=0&&s1[i]<=9)//是数字
            num+=s1[i];
        else other+=s1[i];//其他
    }
    if(word.size()) cout<<word<<endl;//小心不要输出多余的空格
    if(num.size()) cout<<num<<endl;
    if(other.size()) cout<<other<<endl;
    return 0;
} 
 不再想一想吗?

 

 

B. 数位和

单点时限: 2.0 sec

内存限制: 512 MB

“数位和”是指:把一个整数的各位累加后的结果。
例如正整数 123456 的数位和是 1+2+3+4+5+6=21
现在,请你帮忙计算一个整数 n 在 r 进制下的数位和,并用相应的进制输出结果。

输入格式

第 1 行:整数 T(1T10) 为问题数。

第 2 行:第一个问题的数据。包含两个正整数 n(1n2147483647) 和 r(2n16)

第 3 ∽ T+1 行:后面问题的数据,格式与第一个问题相同。

输出格式

对于每个问题,输出一行问题的编号(0 开始编号,格式:case #0: 等),然后在一行中输出用 r 进制表示的十进制正整数 n转换成 r 进制后的数位和。

样例

input
2
123456 10
123456 2
output
case #0:
21
case #1:
110

提示

r进制大于10时,用大写字母A-F表示大于10的数字。

例如:十进制数29转换为16进制为 1D,此时数位和为14,则正确的输出应为:E

两次进制转换即可,当然,好像有一次转换完成的办法。

技术图片
#include<bits/stdc++.h>
using namespace std;
char apl[20]={0,1,2,3,4,5,
        6,7,8,9,A,B,C,D,E,F};//进制转换用
void solve(){
    int num,i(0),r,sum(0);
    char ans[100000]={};
    cin>>num>>r;
    while(num){
        sum+=num%r;
        num/=r;
    }//第一次进制转换
    while(sum){
        ans[i]=apl[sum%r];
        i++;
        sum/=r;
    }//第二次进制转换
    while(--i>=0)//逆序输出
        cout<<ans[i];
    cout<<endl;
}
int main(){
    int t,i;
    cin>>t;
    for(i=0;i<t;i++) {
        printf("case #%d:\n",i);
        solve();
    }
    return 0;
} 
不再想一想吗?

 

 

 

 

C. 谁是第一名

单点时限: 2.0 sec

内存限制: 512 MB

为了知道哪些学生最优秀,考虑他们的三项主要课程(C Programming Language, Mathematics , English)成绩和平均成绩(平均成绩四舍五入取整),分别用C,M,E,A表示。写一个程序,对学生的四项成绩进行排序并输出每项成绩的第一名。程序的输入为学生的学号和三门课程成绩,不包含平均成绩。

输入格式

第 1 行:一个整数 T (1≤T≤10) 为问题数。

对于每组测试数据:

第 1 行:整数 n (1≤n≤100) ,表示学生的数量。

第 2 行~第 n+1 行:每行分别是一个学生的成绩信息,包含学号(长度为 11),三门课程C, M, E的成绩G(G为整数且0<G≤100),各项之间都有一个空格。

输出格式

对于每个问题,输出一行问题的编号(0 开始编号,格式:case #0:等),然后分别输出A,C,M,E各项成绩中排名第一的学生的学号。当成绩并列第一时按按照学号的字典序顺序输出。

样例

input
2
5
10185101101 98 85 90
10185101102 98 95 88
10185101103 82 87 94
10185101104 91 91 91
10185101105 85 90 90
2
10185101103 82 87 94
10185101104 91 91 91
output
case #0:
A:
10185101102
C:
10185101101
10185101102
M:
10185101102
E:
10185101103
case #1:
A:
10185101104
C:
10185101104
M:
10185101104
E:
10185101103

提示

注意A(平均成绩)四舍五入取整数。

一道非常裸的排序题,一波STLsort解决之,注意A是取整数,如果用double反而不能通过。

技术图片
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct stu{//学生结构体
    int c,m,e,a;
    ll id;
};
bool cmpa(stu a,stu b){//比较函数
    if(a.a!=b.a) return a.a>b.a;
    return a.id<b.id;
}
bool cmpc(stu a,stu b){
    if(a.c!=b.c) return a.c>b.c;
    return a.id<b.id;
}
bool cmpm(stu a,stu b){
    if(a.m!=b.m) return a.m>b.m;
    return a.id<b.id;
}
bool cmpe(stu a,stu b){
    if(a.e!=b.e) return a.e>b.e;
    return a.id<b.id;
}
void solve(){
    int n,i;
    stu stud[105];
    cin>>n;
    for(i=0;i<n;i++){
        scanf("%lld%d%d%d",&stud[i].id,
        &stud[i].c,&stud[i].m,&stud[i].e);
        stud[i].a=double(stud[i].c+stud[i].m+stud[i].e)/3+0.49;
    }
    
    sort(stud,stud+n,cmpa);
    cout<<"A:"<<endl<<stud[0].id<<endl;
    i=1;
    while(i<n&&stud[i].a==stud[0].a){//同时第一的输出,注意不要越界
        cout<<stud[i].id<<endl;
        i++;
    }
    
    sort(stud,stud+n,cmpc);
    cout<<"C:"<<endl<<stud[0].id<<endl;
    i=1;
    while(i<n&&stud[i].c==stud[0].c){
        cout<<stud[i].id<<endl;
        i++;
    }
    
    sort(stud,stud+n,cmpm);
    cout<<"M:"<<endl<<stud[0].id<<endl;
    i=1;
    while(i<n&&stud[i].m==stud[0].m){
        cout<<stud[i].id<<endl;
        i++;
    }
    
    sort(stud,stud+n,cmpe);
    cout<<"E:"<<endl<<stud[0].id<<endl;
    i=1;
    while(i<n&&stud[i].e==stud[0].e){
        cout<<stud[i].id<<endl;
        i++;
    }
    return ;
}
int main(){
    int t,i;
    cin>>t;
    for(i=0;i<t;i++) {
        printf("case #%d:\n",i);
        solve();
    }
    return 0;
} 
不再想一想吗?

 

 

 

 

D. 记忆碎片

单点时限: 2.0 sec

内存限制: 512 MB

在徒弟的记忆中,自入门以来,师父的身影似乎一直陪伴在自己身旁。
多年后,徒弟只剩下零碎的记忆片段,不过每段记忆片段却十分的清晰,并且能很明确的说出这段记忆发生在哪一段时间内。
徒弟很想知道自己和师父在一起一共有多少日子,请帮他解决上述问题。

输入格式

包含一个整数N
接下来的N行,每行包含两个不同的整数s, t,表示一段记忆碎片发生的起始时间s和结束时间t。

数据约束
对于60%的数据,N<=500,0<=s<=t<=1000
对于100%的数据,N<=5000, 0<=s<=t<=5000000

输出格式

一个整数,表示徒弟能回忆起的和师父在一起的日子总数

样例

input
3
100 200
150 300
470 471
output
203

提示

对于输入样例:
3
100 200
150 300
470 471


解释:
记忆中和师父在一起的日子为[100,300]和[470,471],一共203天。


 这道题可以说是一道非常经典的贪心题了。
如果不说贪心,可以暴力模拟这个过程,开一个数组,对于每个区间内的数置一,最后把整个区间扫一遍。
显然这样只能过60%。那贪心是怎么操作的呢?我们首先可以看到,对暴力法,后期制约时间的主要因素是遍历区间的每个点,而相比之下更快速的办法是,对每个区间的end-start+1累加,但这样可能会重复计数。所以我们先对所有区间按区间头升序排列,然后开始维护一个当前区间,如果下一个区间的start比当前区间的end值更小,意味着下一个区间和当前区间是重合的,把下一个区间合并入当前区间,否则结算当前区间的长度,并把下一个区间作为新的当前区间。
注意最后一个区间需要特判。

 

技术图片
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
struct p{//区间头尾
    int start,end;
};
bool cmp(p a,p b){
    if(a.start!=b.start) return a.start<b.start;
    return a.end<b.end;
}
int main(){
    int n,i,start,end;
    ll ans(0);
    p part[5005];
    cin>>n;
    for(i=0;i<n;i++) scanf("%d%d",&part[i].start,&part[i].end);
    sort(part,part+n,cmp);
    start=part[0].start;
    end=part[0].end;
    for(i=1;i<n;i++){
        if(part[i].start>end){//不能合并
            ans+=end-start+1;//注意+1
            start=part[i].start;
            end=part[i].end;
        }
        else
            end=max(end,part[i].end);//合并
        
    }
    ans+=end-start+1;//最后一个区间的特判
    cout<<ans<<endl;
    return 0;
} 
不再想一想吗?

 

 

 

 

E. 徒弟的下山之路

单点时限: 2.0 sec

内存限制: 512 MB

终于到了出师的日子,徒弟需要从山顶的纯阳宫找一条花费最少时间的下山之路。
这座山用一个三角形来表示,从山顶依次向下有n层,第i层有i个可移动的位置,出口在最下层的左边。例如有一座5层的山:
        1 
      3  2 
    4  5  6 
  9  1  7  8 
1  1  4  5  6 

此示例中出口为最下层最左边的1所在位置。山上每个数字代表徒弟路过这个位置需要花费的时间,徒弟下山总时间为路径上数字之和。

徒弟每次可以往左、右、左下、右下四个方向移动,例如徒弟位于第3层的数字5时可以向左走到4,向右走到6;也可以向左下走到1,向右下走到7;
又如徒弟位于第3层的数字6时,可以向左走到5,无法向右;也可以向左下走到7,向右下走到8;

输入格式

第一行一个整数n,表示山的高度
接下来n行,第i行有i个整数,表示徒弟经过这个位置需要的时间(路过每个位置花费的时间不超过10)。

数据约束
对于20%的数据,n=4
对于60%的数据,n<=10
对于100%的数据,n<=1000

输出格式

徒弟下山需要花费的最少时间

样例

input
5
1
3 2
4 5 6
9 1 7 8
1 1 4 5 6
output
11

提示

徒弟下山花费的时间为1+3+4+1+1+1=11

一眼看过去感觉就是数字三角形,再仔细一看发现可以向四个方向走......好吧,动态转移方程挺好写的,dp[i,j]=map[i,j]+max(dp[i,j-1],dp[i,j+1],dp[i+1,j],dp[i+1,j+1],但实现起来发现略麻烦。转换一下思路,可以用bfs解决,开一个队列,对队首可以到达的路径枚举,更新最短的路径,并把有更新的放入队中。写完发现这不就是Dijkstra吗?(捂脸

技术图片
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
int mapping[1005][1005],dp[1005][1005];
int directX[5]={0,0,1,1};
int directY[5]={-1,1,0,1};//方向数组
struct point{
    int x,y,time;
}x;
int main(){
    int n,i,j;
    cin>>n;
    for(i=1;i<=n;i++)
    for(j=1;j<=i;j++) scanf("%d",&mapping[i][j]);
    
    x.x=x.y=1;
    x.time=dp[1][1]=mapping[1][1];
    queue<point> que;
    que.push(x);
    while(!que.empty()){//bfs
        x=que.front();
        que.pop();
        if(x.time>dp[x.x][x.y]) continue;
        for(i=0;i<4;i++){
            x.x+=directX[i];
            x.y+=directY[i];
            x.time+=mapping[x.x][x.y];//新结点
            if(mapping[x.x][x.y]&&(dp[x.x][x.y]>x.time||!dp[x.x][x.y])){//(没有越界&&(可以更新||没更新过))
                que.push(x);
                dp[x.x][x.y]=x.time;
            }    
            x.time-=mapping[x.x][x.y];
            x.x-=directX[i];
            x.y-=directY[i];    //还原为原结点
        }
    }

    cout<<dp[n][1]<<endl;
    return 0;
} 
不再想一想吗?

 

 

以上是关于ecnu18级程设实训第一次机考简易解题报告的主要内容,如果未能解决你的问题,请参考以下文章

18.04.28 17年程设考试题

纯C语言|简便方法|解题报告(第18例) ASCII码

华为OD机试真题Python实现简易压缩算法真题+解题思路+代码(2022&2023)

18.06.26 16年程设期末10:游览规划

数据结构荣誉课---第一次实验解题报告

数据结构荣誉课---第一次实验解题报告