湘潭大學2018年軟體工程程式設計實踐第二次模擬考試題解

mMingfunnyTree發表於2018-12-22

這個部落格不再更新,新部落格地址請戳

模考時間: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 <bits/stdc++.h>
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 <bits/stdc++.h>
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 <bits/stdc++.h>
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 <bits/stdc++.h>
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<bits/stdc++.h>
using namespace std;

int main(){
    int m;cin>>m;
    vector<int> v;
    int a;
    while(scanf("%d",&a)!=EOF){
        v.push_back(a);
    }
    sort(v.begin(),v.end());
    vector<int> dp;
    dp.resize(m+10);
    dp[0]=1;
    int ans=0;
    for(int i=0;i<v.size();i++){
        for(int k=m;k>=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<bits/stdc++.h>
using namespace std;
typedef long long ll;
const double eps = 1e-15;

map<ll,ll> mp,mp2;

vector<pair<int,double> >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<n)continue;
        ans.push_back(make_pair(p.first,1.0*p.second/all));
    }
    for(int i=0;i<ans.size();i++){
        printf("%d %.6f",ans[i].first,ans[i].second+eps);
        if(i+1!=ans.size())puts("");
    }

    return 0;
}


評價:
1.總的來說,這套題的質量還行;
2.動態規劃偏多,對難度的把控不是很到位;
3.資料範圍如果不寫清楚會造成很大影響,但是我們可以嘗試二分範圍的方式測試資料範圍;

相關文章