湘潭大学2018年软件工程程序设计实践第二次模拟考试题解

这个博客不再更新,新博客地址请戳

模考时间:2018年12月

希望这篇博客能够帮助到大家。

题目简单分析:


A
任务描述
在湘大xx奶茶店夏天推出了新的饮料价格为5元。
很多学生都要买饮料,每个学生一次只买一个。
然后给你人民币5元、10元或20元。
之后你给每个买了饮料的学生找零。
最初你这里没有钱(当第一个学生拿10元给你你就没办法找零钱)。
如果你能成功找零,返回true,否则返回false。

样例
输入:
5 5 5 10 20
输出:
true

思路:
温暖的签到题。
按照输入直接模拟收银过程便可。
记录一下手中当前有多少张5元,多少张10元便可。
①:收到5元。直接记录便可,因为不需要找零。
②:收到10元。由于要找零5元,那么看手中是否有5元的,然后相应的减一和加一便可。
③:收到20元。找零15元,可以找10+5,也可以5+5+5,但是我们通过生活经历可以知道一定要把整钱找出去,零钱留在手里最好,所以先判断有没有10+5,再判断有没有5+5+5。

#include 
using namespace std;

int main(){
    int cnt5=0,cnt10=0,cnt20=0;
    int ans=1;
    int a;
    while(scanf("%d",&a)!=EOF){
        if(a==5){
            cnt5++;
        }
        else if(a==10){
            if(!cnt5)ans=0;
            else{
                cnt5--;
                cnt10++;
            }
        }
        else{
            if(cnt10&&cnt5){
                cnt10--;cnt5--;
                cnt20++;
            }
            else if(cnt5>=3){
                cnt5-=3;
            }
            else{
                ans=0;
            }
        }
    }
    if(ans)printf("true");
    else printf("false");
    return 0;
}


B
任务描述
工地有n块砖头需要搬运,但由于重量限制,每次只能搬1块或者2块,你能帮工地计算下能有多少种不同的搬运方法吗?

样例
比如n=3,1+1+1=1+2=2+1=3,共有3种不同的方法。

思路:
很裸的初中题:斐波那契数列。
如果不记得结论,可以自己在草稿纸上推出前几项的值,如果能够看出规律,就可以直接写了。

F(n) = F(n-1) + F(n-2) n>1
f(1) = F(0) = 1

#include 
using namespace std;
typedef long long ll;

ll dp[1000];
int main(){
    int n;
    cin>>n;
    dp[0]=1;
    dp[1]=1;
    for(int i=2;i<=n;i++)dp[i]=dp[i-1]+dp[i-2];
    printf("%lld",dp[n]);
    return 0;
}


C
任务描述
这里我们定义一个新的数:对于一个正整数x,再将x每个数位的平方和赋值给x,重复上一步,最后这个数等于1。
你需要写程序判断一个数符不符合这个定义。
如果无限循环但始终变不到1,则输出NO。
如果可以变为1,那么这个数符合定义则输出YES。

样例
19 就符合定义。

1^2 + 9^2 = 82
8^2 + 2^2 = 68
6^2 + 8^2 = 100
1^2 + 0^2 + 0^2 = 1

思路:
我们可以暴力模拟这个过程,因为一个数的平方和肯定是不会太大的,用不着几次就会降低到很小的数字。
这时,如果该数字变成了1,那么输出yes。如果它变成了之前出现过的数字,说明陷入了死循环,肯定是要不得的,这样就直接跳出循环,输出no。

#include 
using namespace std;

int vis[1000000];
int ans=1;
void dfs(int x){
    vis[x]=1;
    int sum=0;
    int xx=x;
    while(xx){
        sum+=(xx%10)*(xx%10);
        xx/=10;
    }
    if(vis[sum])ans=0;
    else dfs(sum);
}

int main(){
    int x;scanf("%d",&x);
    dfs(x);
    if(vis[1])printf("YES");
    else   printf("NO");
    return 0;
}


D unsolved

任务描述
工地有一个n升蓄水池,现在需要将它灌满水(不能溢出),当第i次灌水的时候,可以灌入1至num[i-1]升水。

问有多少种灌满水的方法?答案可能很大,答案对1e9+7取模。

• 1 <= n <= 10^6
• 1 <= num[i] <=10^6

样例
给出n=2,num=[2,2],返回2。

解释:

可以倒入2次1升的水,也可以在第1次倒入2升的水。

给出n=3,num=[3,2,1],返回4

解释:

方案一:第1次倒入3升的水。
方案二:第1次倒入1升的水,第2次倒入2升的水。
方案三:第1次倒入2升的水,第2次倒入1升的水。
方案四:第1次倒入1升的水,第2次倒入1升的水,第3次倒入1升的水。

思路:
对于这个毒瘤题表示恶心。
首先,题面描述有错误,然后数据范围应该是错的。
经过我的测试,n的范围是100,num[i]的范围也是100。
然后我写的动态规划是下面的代码,不清楚为什么会wrong answer。
如果有大佬能够看出错误,请务必指出,谢谢。

我的做法是这样的:
1.用 dp[ i ][ j ] 表示到达第i次灌水的时候,池子里有j升水的方案数。
2.那么,初始值 dp[ 0 ][ 0 ] 我们设为 1 ,表示一开始没有水的方案数为 1 。
3.然后 for 循环枚举 i 和 j ,我们想到,第 i 天的加水量范围是 [ 1, num[i] ],那么我们用 for 循环枚举这个增量 add 就好了,这三层循环的复杂度在题面数据范围下是爆炸的,但是经过测试,在我测的数据范围下是可以通过的。
4. dp[ i ][ j] += dp[ i-1 ][j - add],表示第 i 天如果加入 add 升水,那么蓄水池里有 j 升水的方案数,应该从 前一天有 j - add 升水的方案数转移得到。(注意数组越界)
5. 最后我们把所有 dp[ i ][ n ] 相加便是答案。

#include 
using namespace std;
typedef long long ll;

const int mod = 1e9+7;
ll dp[1005][1050];
int num[1005];
int main(){
    memset(dp,0,sizeof dp);
    int n;
    cin>>n;
    for(int i=1;i<=n;i++){
        scanf("%d",num+i);
    }
    dp[0][0]=1;
    int ans=0;
    for(int i=1;i<=n;i++){
        for(int j=i;j<=n;j++){
            for(int add=1;add<=num[i];add++){
                if(j-add>=0)dp[i][j]+=dp[i-1][j-add];
                dp[i][j]%=mod;
            }
        }
        ans+=dp[i][n];
        ans%=mod;
    }
    printf("%lld",ans);
    return 0;
}


E

任务描述
又快到了寒假时间,说到寒假,免不了出去玩耍,小蒋今天要去郊游,她想带点零食去,但是她的包包大小有限,所以她需要在n个零食中挑选若干零食装入包包,最多能装多满?假设包包的大小为m,每袋零食的大小为A[i]。

样例
如果有4个零食大小是[2, 3, 5, 7]。
如果包包的大小为11,可以选择[2, 3, 5]装入背包,最多可以装满10的空间。
如果包包的大小为12,可以选择[2, 3, 7]装入背包,最多可以装满12的空间。

测试说明
平台会对你编写的代码进行测试:

测试输入:
11
2 3 5 7

预期输出:
10

思路:
01背包问题的简化版本
由于我的代码写成一维滚动数组的形式,所以有个小细节,就是遍历的顺序从大到小。
核心思想是:
dp[ k ] 表示到达当前物品的时候,背包里装了重量为 k 的物品的情况存不存在。
枚举所有物品,到达第 i 个物品的时候,我们枚举“已装重量k”,如果在前面的物品中,我们得到“背包里装了重量为 k 的物品的情况存在”这一信息的话,那么 dp[ k + v[i]] =1 ,表示“到目前为止,背包里装了重量为 k + v[i] 的物品的情况存在”

最后看一下最大存在的重量便可。

#include
using namespace std;

int main(){
    int m;cin>>m;
    vector v;
    int a;
    while(scanf("%d",&a)!=EOF){
        v.push_back(a);
    }
    sort(v.begin(),v.end());
    vector dp;
    dp.resize(m+10);
    dp[0]=1;
    int ans=0;
    for(int i=0;i=0;k--){
            if(dp[k]&&k+v[i]<=m){
                dp[k+v[i]]=1;
                ans=max(ans,k+v[i]);
            }
        }
    }
    printf("%d",ans);
    return 0;
}


F

任务描述
小明他抛了n个骰子,抛出来之后显示的数是x1,x2,x3…xn;

令Z=x1+x2…+xn。(比如抛两个骰子 ,显示2,5。那么n=2,Z=7)

给定骰子个数n,计算出所有有几率出现的Z值以及他出现的可能性(几率)。

概率用double类型,保留6位小数。
编程要求
抛出n个骰子,抛出来之后显示的数是x1,x2,x3…xn;

令Z=x1+x2…+xn。

计算出所有Z值以及他出现的可能性(几率)。

测试说明
平台会对你编写的代码进行测试:

测试输入:1

预期输出:
1 0.166667
2 0.166667
3 0.166667
4 0.166667
5 0.166667
6 0.166667

思路:
首先我们知道,骰子有6个面,每次投掷每个面的概率都是0.166667。
我们直接模拟便可:
定义 i : pair( x , y ) 表示第 i 次投掷可以得到总和 x 的方案数 y 。
第1次:
(1,1) (2,1)(3,1)(4,1)(5,1)(6,1)

我们不妨按照这个过程模拟一下,就可以很轻松得到答案啦。

依旧是一个简单的状态转移过程,可以说是动态规划吧。
详细的不想说了,有兴趣看的自己看代码吧。

#include
using namespace std;
typedef long long ll;
const double eps = 1e-15;

map mp,mp2;

vector >ans;

int main(){
    //freopen("a.out","w",stdout);
    int n;cin>>n;
    if(n==1){
        for(ll i=1;i<=5;i++)printf("%lld 0.166667\n",i);
        printf("%lld 0.166667",6);
        return 0;
    }
    double all = pow(6,n);
    for(int i=1;i<=6;i++)mp[i]=1;
    for(int i=2;i<=n;i++){
        mp2.clear();
        for(auto p:mp){
            for(int add=1;add<=6;add++){
                mp2[p.first+add]+=mp[p.first];
            }
        }
        mp.clear();
        for(auto p:mp2)mp[p.first]=p.second;
    }

    for(auto p:mp){
        if(p.first

评价:
1.总的来说,这套题的质量还行;
2.动态规划偏多,对难度的把控不是很到位;
3.数据范围如果不写清楚会造成很大影响,但是我们可以尝试二分范围的方式测试数据范围;

你可能感兴趣的:(XTU—程序设计实践网站)