GDUFS 2017信息学院程序设计新手赛(热身赛)题解

Problem A: 小明的简单任务


Description

小明是思科信息学院的一名新同学,最近他刚开始学习编程。今天他遇到了一个问题:求n个数的和。你能帮他解决这个问题吗?

Input

输入的第一行是一个整型数T(0<T<100),代表有T组测试数据。接下来会有T行,每一行包含一组测试数据。每组测试数据由n+1个整数组成,第一个数为n(0<n<20),接下来会有n个数a1,a2...an(0<=ai<=100)。

Output

求出每组测试数据里面n个数(a1,a2...an)的和然后输出,每组数据的结果输出占一行。

Sample Input

2
4 1 2 3 4
3 10 12 18

Sample Output

10
40


题解:简单的输入输出练习。代码用C++描述,其他语言类似。下同。


#include<iostream>
using namespace std;

int T,ans,n,temp;

int main(){

    cin>>T;
    while(T--){
        ans=0;
        cin>>n;
        for(int i=0;i<n;i++){
            cin>>temp;
            ans+=temp;
        }
        cout<<ans<<endl;
    }

    return 0;
}


Problem B: 2048


Description

K下课回到宿舍,发现宿舍的人都出去了,但他没有带钥匙,只好在寒风中玩起了2048

热爱编程的他心血来潮,这些数字不都是2的n次幂吗,那。。。。。

Input

输入一个数字T(0<=T<=20)代表幂

Output

输出2T次方的值

Sample Input

10

Sample Output

1024


题解:由于T很小,32位整形可以存下,所以直接算即可。这里有两个技巧,详细看代码。


#include<iostream>
#include<math.h>
using namespace std;

int T;

int main(){

    cin>>T;
   // cout<<int(pow(2,T))<<endl;// 系统函数实现
   // cout<<(1<<T)<<endl;//位运算实现

    int ans=1;
    for(int i=0;i<T;i++)
        ans*=2;
    cout<<ans<<endl;

    return 0;
}


Problem C: 好基友,玩游戏


Description

Alice和Bob是ACM届里的两位名人,他们总是玩各种奇葩的游戏,然后叫人帮他们分析这个游戏怎么样去玩才能必胜。今天他们两个又玩游戏了! 游戏规则是这样的,N枚硬币排成一个圈,Alice和Bob轮流从中取走一枚或两枚硬币。不过取两枚时,所取的两枚硬币必须是连续的。硬币取走之后留下空位,相隔 空位的硬币视为不连续的。Alice开始先取,取走最后一枚硬币的一方获胜。当双方都采取最优策略时,谁会获胜?

Input

输入的第一行为一个正整数T(T <= 1000),表示有T组数据。 接下来每组数据有一行,为一个正整数N(1≤N≤10000)

Output

对于每组数据,输出获胜的人

Sample Input

2
1
3

Sample Output

Alice
Bob


题解:就是我们平时小时候玩的游戏。稍微推理一下,就可以得到,当N<=2时,Alice必胜,其他情况都是Bob赢。怎么得到的呢?


先来看看最简单的情况:

O    (Alice胜)…………①

OO  (Alice胜)…………②


OOO (Bob胜)…………③

OOOO(Bob胜)…………④


对于第四种情况,如果Alice取边缘的两个,就会转换为②的Bob先手态,所以Bob必胜。如果Alice取对角两个,使得剩下两个孤立,实际上就转化为两场①状态的游戏,还是Bob胜。


OOOOO(Bob胜)…………⑤


对于第五种情况。无论Alice怎么取,Bob都可以通过某种策略,使得局势最终都会转化为上述的四种情况之一。读者可以自己尝试下。其他情况同理。所以N>2时Bob必胜。



#include<iostream>
#include<math.h>
using namespace std;

int T,n;

int main(){

    cin>>T;
    for(int i=0;i<T;i++){
        cin>>n;
        if (n<=2)
            cout<<"Alice"<<endl;
        else
            cout<<"Bob"<<endl;
    }
    return 0;
}


Problem D: 古风排版


Description

中国的古人写文字,是从右向左竖向排版的。本题就请你编写程序,把一段文字按古风排版。

Input

输入在第一行给出一个正整数N(<100),是每一列的字符数。第二行给出一个长度不超过1000的非空字符串,以回车结束。

Output

按古风格式排版给定的字符串,每列N个字符(除了最后一列可能不足N个)

Sample Input

4
This is a test case

Sample Output

asa T
st ih
e tsi
 ce s


题解:这道题主要考察大家的整行输入和二维数组的运用。具体方法的读取整行方法可以百度一下。代码有详细的注释。需要注意的是,剩余的空位要用空格填充。另外控制换行的信息一定要准确。比较绕,希望大家能绕的过来。


#include <iostream>
#include <string.h>
#include <stdio.h>
using namespace std;

int main()  {
    char a[1005],b[1005][105];
    int la,n,m,i,j,i1;
    scanf("%d",&n);
    getchar();//把回车符吃掉,否则下一句会出错
    gets(a);  //读取整行
    la=strlen(a);  //获得长度

    //记录行数
    if(la%n==0)
        m=la/n;
    else
        m=la/n+1;

    for(i=0;i<n;i++){
        i1=i;//控制第几个字符
        for(j=0;j<m;j++){
            if(i1>=la)
                b[i][j]=' ';//剩余的用空格填充
            else
                b[i][j]=a[i1];
            i1+=n;
        }
    }


    //循环输出每一个字符
    for(i=0;i<n;i++){
        for(j=m-1;j>=0;j--){
            printf("%c",b[i][j]);
        }
        printf("\n");
    }

    return 0;
}


Problem E: 判断质数


Description

输入一个质数,现在请你判断是否是质数。如果是的话输出"Yes"否则输出"No"。
一个自然数是质数当且仅当其因子只有1和其本身。

Input

输入有多组数据,第一行为一个整数Q,表示有Q组数据。(1<=Q<=10)

接下来有Q行,每行一个整数n,为待判断的数字。(1<=n<=1000000000)

Output

输出Q行,每行输出Yes或者No,表示是否是质数。注意大小写。

Sample Input

3
5
6
7

Sample Output

Yes
No
Yes


题解:很简单的数学问题,我们很容易就会想到,对于一个数,我们枚举所有可能的约数,然后看看能不能被整除,一旦找到一个,那么就不是质数。于是我们有如下伪代码


FOR i=2 i<N i++

IF  N mod i = 0

Return False

End IF

End FOR

Return True;


但是这么做是不现实的,如果N很大,比如题目中的10^9,就会超时(计算机1s最多算10^7次加法运算)。因此我们要对我们的算法进行优化。我们很容易想到枚举的时候不用全部枚举。举个例子   11肯定不能被6整除(大于了11的一半)。因此我们只用枚举一半。FOR i=2 i<N/2 i++  。但是这个优化是远远不够的。再做思考我们发现,实际上只用枚举到N的平方根即可!因为 假设39能被13整除,那么39肯定能被3整除。所以……用系统函数求求平方根,枚举即可。


#include<iostream>
#include<string.h>
#include<stdio.h>
#include<math.h>
using namespace std;

bool judge(int x){
    for(int i=2;i<sqrt(x);i++)
        if(x%i==0)
            return false;
    return true;
}

int main()  {

    int Q;
    cin>>Q;
    while(Q--){
        int n;
        cin>>n;
        if(judge(n))
            cout<<"Yes"<<endl;
        else
            cout<<"No"<<endl;
    }

    return 0;
}


Problem F: 发洪水啦


Description

有一块由N*N(1<=N<=1000)的小格子组成的土地。有些地方(格子)是山地用'*'表示,有些地方(格子)是平原,用'.’表示。 一天最左上角的格子发洪水了(最左上角格子一定是平原)。如果一个格子有洪水,那么它会蔓延至其他4个方向(上下左右)的格子,除非那个格子为山地。 问有多少个平原会被淹没。

Input

每组测试数据第一行包含一个整数N,表示土地大小 接下来N行参见样例

Output

一行一个整数,表示有多少个平原会被淹没

Sample Input

5
..*..
.*.*.
...**
***..
.....

Sample Output

7


题解:刚接触编程的同学对这题可能无法下手。实际上这道题,只要你对递归调用函数很熟悉的话,应该是很容易的。我们可以模拟洪水泛滥的过程。用一个递归函数,不断的改变一个格子周围四个格子的状态。直到不能被改变为止。递归都很难解释清楚,希望大家看代码能明白。这道题也可以用朴素的做法,但是会有很多的判断条件。


#include<iostream>
#include<string.h>
#include<stdio.h>
#include<math.h>
using namespace std;

char maze[1005][1005];
int ans;

//用水淹没
void dfs(int x,int y){
    ans+=1;//答案+1
    maze[x][y]='1';//1代表该格子已经被水淹没了
    
    //看看周围四个格子是不是平原,是的话就用水淹没它
    if(maze[x+1][y]=='.')
        dfs(x+1,y);
    if(maze[x-1][y]=='.')
        dfs(x-1,y);
    if(maze[x][y+1]=='.')
        dfs(x,y+1);
    if(maze[x][y-1]=='.')
        dfs(x,y-1);
}

int main()  {
    
    int N;
    cin>>N;
    for(int i=1;i<=N;i++)
        for(int j=1;j<=N;j++)
            cin>>maze[i][j];
    ans=0;
    dfs(1,1);//用水淹没第一个格子
    cout<<ans<<endl;
    
    return 0;
}


Problem G: 判断斐波那契数


Description

斐波纳契数列是这样的数列: f(1) = 1 f(2) = 1 f(3) = 2 f(4) = 3 .... f(n) = f(n-1)+f(n-2) 即从第3项开始,其值等于前两个斐波那契数之和,例如f(3) = f(2)+f(1) = 1+1 = 2 那么现在输入一个数n,判断它是否在斐波那契数列之中

Input

输入一个正整数n(n<=1000000)

Output

如果它在斐波那契数列中则输出 YES 否则输出 NO

Sample Input

5

Sample Output

YES

题解:首先你要知道什么是斐波那契数列,他有一个性质就是,增长得特别快。因此观察题目数据N<=10^6,我们可以直接枚举所有情况就可以了。当然通用的做法是,自己把斐波那契数列求出来,相信大家做作业的时候都做过了。


#include<iostream>
#include<string.h>
#include<stdio.h>
#include<math.h>
using namespace std;

int main(){
    
    int n;
    cin>>n;
    
    if(n==1||n==2||n==3||n==5||n==8||n==13||n==21||n==34||n==55||n==89||n==144||n==233||n==377||n==610||n==987||n==1597||n==2584||n==4181||n==6765||n==10946||n==17711||n==28657||n==46368||n==75025)
        
        cout<<"YES"<<endl;
    else
        cout<<"NO"<<endl;
    
    return 0;
}


Problem H: 求阶乘


Description

数学函数也可以递归定义。例如,阶乘函数f(n) = n!可以定义为:

f(1) = 1

f(n) = f(n-1)*n ( n >= 1)

Input

输入一个整数n

Output

输出f(n)的值

Sample Input

5

Sample Output

120

题解:这题非常简单,不多解释了。重点在于数据范围,我们知道,阶乘的增长是爆炸性的,因此我们要用更大的数据类型去存储答案。int不够,我们就用long long int,还不够,那就只能用其他方法了。


#include<iostream>
using namespace std;
int main(){
    
    long long int n;
    cin>>n;
    long long int ans=1;
    for(long long int i=1;i<=n;i++)
        ans*=i;
    cout<<ans<<endl;
    
    return 0;
}


Problem I: 贪心的姐姐


Description

姐姐最近沉迷于挖矿,但是由于各种因素的限制,姐姐在同一个地方只能挖一次,而且下一次挖矿的位置只能位于此次所在位置的右方或者下方(姐姐一开始位于地图的左上方),你可以帮姐姐计算一下在一次愉快的挖矿旅程中他最多能挖到多少价值的矿吗?
挖矿区域可以视为正方形,每个格子对应着一次能挖到的矿石的价值,例如
在3*3的正方形中

1 3 3
2 1 3
2 2 1

姐姐所能挖到矿的最大价值为:11(1+3+3+3+1,从(1,1)->(1,2)->(1,3)->(2,3)->(3,3))

Input

第1行:N,N为矩阵的大小。(2 <= N <= 500)
第2 - N + 1行:每行N个数,中间用空格隔开,对应格子中矿石的价值。(1 <= N[i] <= 10000)

Output

输出娴姐能够获得的最大价值。

Sample Input

3
1 3 3
2 1 3
2 2 1

Sample Output

11

题解:这道题咋一看,觉得很简单,对于向右和向下走,我们每次都走值较大的那个,如题目那样。但是稍微想想,就会发现这种策略是错的!比如:

1 3 3

1 1 3

9 2 1

对于一开始,用我们刚刚的策略会往右走,这样子就永远的错过了9了。

这样的话我们可不可以每一次,都判断向下走的和,和向右走的和,看看哪个大走哪个呢?明显是不行的。

1 3 6

1 1 4

9 2 1

用刚刚的策略会往下走,这样子就错过了4了。

还是不行!那么我们就暴力吧!把所有情况都算一遍,找出最大值!复杂度为,2^500。更加不行!那怎么办……再想想,好像暴力其实也可以?我们不必把所有的情况都算一遍,比如

1 3

1 1

我们知道右到下是最优的,下到右是坏的,因此下到右再做任何动作,都不会比右到下优。所以通过这个性质,我们直接把复杂度降到了N^2。对于每一种走法我们只走最优的,那么怎么记录最优的呢?我们直接用一个数组保存最优答案即可。然后我们从左上开始模拟走的情况(即两个for循环),一步一步的把最优答案保存。实际上这是一个叫做动态规划的算法,我们走的每一步,都是从上一步最优的情况得来。详细看代码解释。还不懂的读者,可以自己在草稿纸上,模拟一遍整个数组的填写过程。


#include<iostream>
using namespace std;
const int MAX_N=550;

int G[MAX_N][MAX_N];//最优值数组
//如G[i][j]=10的意思就是,当贪心姐姐走到第i行第j列时她能得到的最大价值为10


int main(){
    
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            cin>>G[i][j];//输入
       
    //模拟走的每一种情况
    for(int i=1;i<=n;i++){
        for(int j=1;j<=n;j++){
            G[i][j]=max(G[i-1][j]+G[i][j],G[i][j-1]+G[i][j]);//这一步是从上走下来优呢,还是从左走过来优呢?
        }
    }
    
    //这个就是答案
    cout<<G[n][n]<<endl;
    
    return 0;
}


Problem J: 好专业,割绳子


Description

小李专业割绳子三十年,如果你给他N条绳子,长度分别为Li,并告诉他要割成K条长度相同的绳子,那么小李肯定能最优的割成K条绳子,并保证它们肯定是最长的!

Input

输入的第一行为一个正整数T(T <= 10),表示有T组数据。
接下来每组数据有三行。
第一行为一个正整数N(1≤N≤10000)。
第二行为一个正整数K(1≤K≤10000)。
第三行为 Li,中间用空格隔开 (1≤Li≤100000)。

Output

对于每组数据,输出最长的长度(答案保留到小数点后两位)

Sample Input

2
4
11
8.02 7.43 4.57 5.39
6
10
3 4 5 6 7 5

Sample Output

2.00
2.50

题解:这题需要一定的算法基础,才能做出,因此在这里不做详细的解释。做这题前首先要理解二分搜索算法。对于有序的数组,我们可以通过二分,在很快的时间内找到你要找的数。比如 1 2 3 4 5 6 7,你要找6,我们先从中间开始,中间为4,6比4大,因此6一定在4的右边,所以我们就可以递归查找,问题变为,在 5 6 7 中找6 。然后我们就找到了。如果找的是1,我们就变为在1 2 3 中找1,然后再在1 中找 1。相信读者都能明白这个道理。那么回到题目,我们要找到一个最长的长度,使得剪出来刚好有K段。那么我们把答案想象成一个有序的序列  1  2  3  4  5  6  7  8  9  我们先假设,5就是答案,那么我们就看看题目给的绳子,把他们都尽可能的切成5,看看能不能凑成K段。这个时候有两种情况,一种是凑成了>=K段,一种是<K段。那么如果是大于等于K段,那么证明我们的答案小了,可能有更大的一个长度,使得刚好是K段。(等于K段的话,实际上我们还能找到更长的使得可以切成K段,因此我们把大于等于K段一起考虑)第二种情况是<K段,证明我们的答案太大了,我们要缩小我们的答案。聪明的读者应该想到了。这其实可以运用二分搜索来搜索答案,不断地使答案更加逼近最长长度。因此我们可以通过二分枚举答案的做法来完成这道题。详细的细节请看代码。


#include<iostream>
#include<stdio.h>
#include<algorithm>
#include<math.h>
using namespace std;
#define MAX 10000
#define INF 10000000

bool panduan(double x,int n,int k,double Li[]){
    int num =0;
    for (int i=0;i<n;i++){
        num+=int(Li[i]/x);
    }
    return num>=k;
}

int main(){
    int n;
    cin>>n;
    int a[n],b[n];
    double num[n];//记录答案

    for(int i=0;i<n;i++){
        cin>>a[i]>>b[i];
        double Li[MAX];
        for(int j=0;j<a[i];j++)
            cin>>Li[j];
        
        double lb=0,ub=INF;//把答案的下界设为0,上界设为一个足够大的数

        //100次二分足以!
        for(int j=0;j<100;j++){
            double mid =(lb+ub)/2;
            if(panduan(mid,a[i],b[i],Li))//如果能切成K段,
                lb=mid;//尝试把答案放大
            else
                ub=mid;//尝试把答案缩小
        }

        num[i]=floor(int(ub*100))/100;//保留两位小数
    }

    for(int i=0;i<n;i++){
        printf("%.2f\n",num[i]);
    }
    return 0;
}


Problem K: 好数学,求阶乘


Description

表达式N!称为N的阶乘,是指前N个正整数的乘积,其中N是正数。现在给出N,请计算N的阶乘最右一位非0数字。

Input

输入的第一行为一个正整数T(T <= 1000),表示有T组数据。 接下来每组数据有一行,为一个正整数N(1≤N≤10000)

Output

对于每组数据,输出最右一位非0数字。

Sample Input

4
1
2
5
9999

Sample Output

1
2
2
8

 

题解:这道题由于数据范围较小,因此有简单的做法。我们都知道123000乘任何数,后面的三个0都不会变,变得只是前面的123,有时候还会生成多几个0。因此我们每算一次就把后面的所有0都截掉。最后剩下的取第一位就行了。这里有一个问题,就是溢出问题。阶乘是一个很大的数,long long int也无法存下,但是我们只关心后面的数字,前面的因数字是什么我不关心,此我们要剪掉前面无用的数字。具体剪掉多少呢?数据范围较小,只要剪足够少就可以了。用取模运算即可剪,具体看代码。当然这道题有更通用的做法,我们都知道2*5等于10,所以因子2和5会产生0,因此我们要剔除所有的2,5因子,具体不解释了。

#include <iostream>
using namespace std;
int main(){
    int T;
    long long int sum,N;
    cin>>T;
    while(T--){
        cin>>N;
        sum=1;
        for(int i=1;i<=N;i++){
            sum=sum*i;
            while(sum%10==0){
            sum=sum/10;
            }
            sum=sum%1000000;
        }
        sum=sum%10;
        cout<<sum<<endl;
    }
}



谢谢阅读!





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值