湘潭大學2018年上學期程式設計實踐模擬考試3 參考題解

mMingfunnyTree發表於2018-06-21

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

體驗1
軍神太強啦,1小時屯6題,瞬間AK,接下來的90分鐘一直在跟榜

體驗2
A題原題,迴圈寫得好就不麻煩,不然要寫很多行,情況要想全並不難。
B題原題,有了上一場的提示之後,這題就不難了。
C題很簡單(小聲)。
D題卡掉了O(TNK*log(N))的方法,卡掉我5發logN ,不過還是可做。
E題水dp(組合數學)。
F題原題,記憶化搜尋。

體驗3
被DC兩題卡到,認識到自己是個菜雞選手。
D:
一開始寫O(NN)的預處理 + map存答案
改成O(N
KlogN)的二分查詢
再靈機一動想到了O(N
K)的做法
有驚無險
C:
一開始看成了卡特蘭數,後來發現不對
沒帶筆紙,找學霸借了筆和紙後開始推公式
發現規律後一頓猛敲,然而蜜汁wa
結束後發現自己迴圈邊界寫錯了
再也不貪圖方便擅自改迴圈邊界了

體驗4
難度較上一場而言,難題偏簡單,簡單題偏難,很適合我這種菜雞選手。

懶得說了,直接上題解吧。
由於172.22.112.249/exam進不去,看不到題面了


A
題意:給一個44的矩陣,外圈的格子可以旋轉,每次轉動1格,求最大22的小矩陣和。
思路:暴力。

#include <bits/stdc++.h>
using namespace std;

int main(){
    int t;cin>>t;
    int a[20];
    int b[]={6,7,10,11};
    int bg[]={1,2,3,4,8,12,16,15,14,13,9,5};
    while(t--){
        for(int i=1;i<=16;i++)cin>>a[i];
        int ans=a[6]+a[7]+a[10]+a[11];
        for(int i=0;i<4;i++){
            for(int j=0;j<12;j++){
                ans = max(ans , a[bg[(j)%12]]+a[bg[(j+1)%12]]+a[bg[(j+2)%12]]+a[b[i]]);
            }
        }
        for(int i=0;i<12;i++){
            ans = max(ans , a[bg[(i)%12]]+a[bg[(i+1)%12]] + a[6] + a[7]);
            ans = max(ans , a[bg[(i)%12]]+a[bg[(i+1)%12]] + a[11] + a[7]);
            ans = max(ans , a[bg[(i)%12]]+a[bg[(i+1)%12]] + a[10] + a[11]);
            ans = max(ans , a[bg[(i)%12]]+a[bg[(i+1)%12]] + a[6] + a[10]);
        }
        cout<<ans<<endl;
    }
    return 0;
}

B
題意:找到最小的b,使得n在b進位制下,數位上出現的每個數字的次數都相等。
思路:已知一定有解,不妨暴力列舉。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n;

bool check(ll a,ll b){
    int vis[65]={0};
    while(a){
        vis[a%b]++;
        a/=b;
    }
    int v=-1;
    for(int i=0;i<65;i++){
        if(v==-1){
            if(vis[i])v=vis[i];
        } else {
            if(vis[i] && vis[i]!=v)return 0;
        }
    }return 1;
}

int main(){
    int t;cin>>t;
    while(t--){
        cin>>n;
        for(int b=2;;b++){
            if(check(n,b)){
                cout<<b<<endl;break;
            }
        }
    }
    return 0;
}

C
題意:n個點的SB樹有多少種(對1e9+7取模)?
思路:
首先我們列舉0到10的所有情況,發現:
n h[n]
0 1
1 1
2 2
3 1
4 4
5 4
6 4
7 1
8 8
9 16
10 32

做這種題,我喜歡先猜結論,再證明正確性。
畫圖的時候(這裡沒有圖,要圖自己畫),我們發現這棵樹最底層是可以活動的,
而所有非底層都要填滿,所以對於2-1,4-1,8-1,這些值,答案一定為1。
那麼我們就可以求出最底層有多少個可以活動的點。
因為要保持平衡,假設有rest個可以活動的點,那麼我可以左邊放rest/2個,剩餘的放在右邊。
這樣我們發現,如果rest/2 != rest - rest/2的話,左右兩棵樹可以交換,所以結果還要*2。

那麼可以在O(N)的複雜度下,算出前n項的結果。
其實可發現,rest/2對應的那棵樹,跟前面算出來的樹有著相同的分類方法。
自己畫圖體會吧

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll mod  = 1e9+7;

int main(){
    ll h[1050];
    h[0]=1;
    for(int i=1;i<=1000;i++){
        int l=i/2,r=(i-1)/2;
        if(l==r)
            h[i]=h[l]*h[r]%mod;
        else h[i]=2*h[l]*h[r]%mod;
    }
    int t;cin>>t;
    while(t--){
        int n;cin>>n;
        cout<<h[n]<<endl;
    }
    return 0;
}


D
題意:給一個序列a,給k次查詢,每次給一個s,問有多少個區間[l,r],使得區間和等於s。
思路:
首先想到了O(N^2)求出所有的s,然後map記錄下來,N=1e4 ,明顯要超時。
然後想到對於k個查詢,每次都遍歷左邊界,通過字首和二分的方式找到右邊界,
如果左邊界到右邊界這一段的和為s,答案+1,複雜度O(KNlogN)
還能把這個logN去除掉,用到的是類似於滑動窗戶的方法。複雜度O(K*N)

#include <bits/stdc++.h>
using namespace std;

template<class T>
inline void read(T &ret){
    ret=0;
    char c=getchar();
    while(c>'9'||c<'0')c=getchar();
    ret = ret*10+c-48;
    while(c=getchar(),c>='0'&&c<='9'){
        ret=ret*10+c-48;
    }
}

int pre[10005];
int n,k,s,ans;
int a[10005];

int main(){
    int t;read(t);
    while(t--){
        read(n);read(k);
        pre[0]=0;
        for(int i=1;i<=n;i++){
            read(a[i]);
            pre[i]=pre[i-1]+a[i];
        }
        while(k--){
            read(s);
            ans=0;
            int l=1,r=1;
            while(l<=n&&r<=n){
                if(pre[r]-pre[l-1]==s){
                    ans++;
                    l++;r++;
                }
                else if(pre[r]-pre[l-1]>s){
                    l++;
                } else {
                    r++;
                }
            }
            cout<<ans<<'\n';
        }
    }
    return 0;
}

E
題意:問有多少種到達(n,0)的波形。
思路:
很直觀的dp,或者用組合數也能做,仁者見仁。
首先到達(0,0)的方法有1種。
到達(x,y)的方法有多少種呢,(x-1,y),(x-1,y-1),(x-1,y+1)都能到達(x,y)
種類數滿足加法原理。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

int main(){
    ll dp[110][110];
    int t;cin>>t;
    while(t--){
        memset(dp,0,sizeof dp);
        int n;cin>>n;
        dp[0][0+50]=1;
        for(int x=1;x<=n;x++){
            for(int y=2;y<=100;y++){
                dp[x][y]=max(dp[x][y],dp[x-1][y]+dp[x-1][y-1]+dp[x-1][y+1]);
            }
        }
        cout<<dp[n][50]<<endl;
    }
    return 0;
}

F:
原題,這篇部落格裡有。
https://blog.csdn.net/mmingfunnytree/article/details/78806763

寫的不對的地方歡迎指正,轉載請註明出處^ ^.

相關文章