2020中南大學研究生招生夏令營機試題題解

2020中南大學研究生招生夏令營機試題

第一題:缺失的彩虹

題意

顏色共有七種,給定 n(n100)n(n≤100) 個顏色,問七種顏色中哪些沒有出現。

思路

開一個大小爲 77 的數組,分別統計七種顏色出現次數,最後看看哪些出現次數爲 00 即可。

代碼

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

int main() {
    int n;
    string s;
    while(cin>>n) {
        vector<int> cnt(7,0);
        for(int i=1; i<=n; ++i) {
            cin>>s;
            if(s[0]=='r') cnt[0]++;
            else if(s[0]=='o') cnt[1]++;
            else if(s[0]=='y') cnt[2]++;
            else if(s[0]=='g') cnt[3]++;
            else if(s[0]=='c') cnt[4]++;
            else if(s[0]=='b') cnt[5]++;
            else if(s[0]=='p') cnt[6]++;
        }
        vector<char> ans;
        for(int i=0; i<7; ++i) if(cnt[i]==0) {
            ans.push_back(char('A'+i));
        }
        cout<<int(ans.size())<<'\n';
        for(char p: ans) cout<<p<<'\n';
    }
}

第二題:最小价值和

題意

給定 n(n1e5)n(n≤1e5) 個整數數對 (a,b)(0a,b1e9)(a,b)(0≤a,b≤1e9),有一個長度也爲 nn 的數組。

若將某個數對放在數組的第 ii 個位置上,則其價值爲:a(i1)+b(ni)a*(i-1)+b*(n-i)

求將這些數對分配在數組上後所有價值之和的最小值。

思路

這是一種常見的考察排序、貪心的題目。

我們需要的就是給這些數對排個序,而排序就得知道兩個數對的“大小關係”,因此我們來考察一下兩個數對:

a1,b1a_1,b_1ii 位置,a2,b2a_2,b_2jj 位置,不妨假設 i<ji<j,現在我們僅僅考慮這兩個數對交換前後分別產生的價值,並且他們是否應該交換位置不會影響其他數對的價值。

若:a1(i1)+b1(ni)+a2(j1)+b2(nj)>a1(j1)+b1(nj)+a2(i1)+b2(ni)a_1*(i-1)+b_1*(n-i)+a_2*(j-1)+b_2*(n-j)>a_1*(j-1)+b_1*(n-j)+a_2*(i-1)+b_2*(n-i)

則:(a1a2)(i1)+(b1b2)(ni)>(a1a2)(j1)+(b1b2)(nj)(a_1-a_2)*(i-1)+(b_1-b_2)*(n-i)>(a_1-a_2)*(j-1)+(b_1-b2)*(n-j)

則:(a1a2)(ij)>(b1b2)(ij)(a_1-a_2)*(i-j)>(b_1-b_2)*(i-j)

則:a1b1<a2b2a_1-b_1<a_2-b_2(由於i<ji<j,因此不等號方向改變)

上述推導表明若處於靠前的 ii 位置的數對需要和靠後的 jj 位置的數對發生交換,則應該滿足前者的 aba-b 較小。

也就是說:aba-b 較大的數對應該放得更靠前!

這樣,我們就可以設計排序函數了,通過 aba-b 的大小來決定兩個數對的優先關係。

代碼

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

const int maxn = 1e5+7;

struct Pair{
    int a, b;
    friend bool operator < (const Pair &A, const Pair &B) {
        return A.a-A.b>B.a-B.b;
    }
}p[maxn];

int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    int n;
    while(cin>>n) {
        for(int i=1; i<=n; ++i) {
            cin>>p[i].a>>p[i].b;
        }
        sort(p+1,p+1+n); //sort就完事
        ll ans=0;
        for(int i=1; i<=n; ++i) {
            ans+=ll(i-1)*p[i].a+ll(n-i)*p[i].b; //小心爆int啦
        }
        cout<<ans<<'\n';
    }
}

第三題:PIPI上學路

題意

有一個 nm(n,m5000)n*m(n,m≤5000) 的矩形,PIPI每次只能從矩形的某一個點向右走或向下走。

問從 (x1,y1)(x_1,y_1)(x2,y2)(x_2,y_2) 共有多少種走法?(答案對1e9+71e9+7取模)

難點:多組數據 + 每組數據 q(q5000)q(q≤5000) 個詢問,因此總詢問次數可能非常多!

思路

由於詢問次數非常多,那我們能提前處理好所有可能的詢問就好了。

考慮 (x1,y1)(x_1,y_1)(x2,y2)(x_2,y_2) 之間的所有方格構成了一個矩形,並且此矩形在原 nmn*m 矩形中位置不影響方案數,只有這個矩形的尺寸決定方案數。

因此,我們預處理 nmaxnmax(nmax=5000)n_{max}*n_{max}(n_{max}=5000) 的矩形即可,預處理:

設:ways[i][j]ways[i][j]表示從 (1,1)(1,1) 走到 (i,j)(i,j)處的方案數。

則:ways[i][j]=ways[i1][j]+ways[i][j1]ways[i][j]=ways[i-1][j]+ways[i][j-1]

處理好後,對於每次的詢問可以直接 O(1)O(1) 回答:ways[x2x1+1][y2y1+1]ways[x2-x1+1][y2-y1+1]

時間複雜度:O(nn+Tq)O(n*n + T * q),空間複雜度:O(nn)O(n*n)

(PS:儘量不要使用cin和cout,否則輸入輸出速度太慢,自行優化)

代碼

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

const int maxn = 5e3+7;
const int mod = 1e9+7;

int ways[maxn][maxn];

int main() {
    ways[1][0]=1; //一個假想的入口(1,0),方便對ways[1][1]進行賦值
    for(int i=1; i<maxn; ++i) {
        for(int j=1; j<maxn; ++j) {
            ways[i][j]=(ways[i-1][j]+ways[i][j-1])%mod;
        }
    }
    int n, m, q;
    ios::sync_with_stdio(false); cin.tie(0); //這裏通過採用取消輸入輸出同步的方式加快讀入
    while(cin>>n>>m>>q) {
        while(q--) {
            int x1, y1, x2, y2;
            cin>>x1>>y1>>x2>>y2;
            cout<<ways[x2-x1+1][y2-y1+1]<<'\n'; //不要cout<<endl,endl會刷新輸出流緩存區,相對於'\n'時間代價是非常高的
        }
    }
}

第四題:最大容量和

題意

mm 根木棍,m=nk(n,k1e5)m=n*k(n,k≤1e5)nn 個桶,每個桶由 kk 根木棍構成,桶的容量由最短的木棍長度決定,桶的底面積爲 11,木棍長度不大於 1e91e9

現要求最大的桶和最小的桶容量差小於等於 LL,問 nn 個桶的最大容量和。

如果無法滿足組成 nn 個桶,輸出 00

思路

由於桶的容量由構成它的最短木棍決定,因此我們希望那些決定桶的容量的木棍就儘可能長。

在不考慮 LL 的情況下,我們只需要將所有木棍拍個序,然後從較大的到較小的依次選擇木棍,每有 kk 根木棍,我們就拿他們構建一個桶。

顯然,這樣構建的 nn 個桶容量都是儘可能大了。

然後再來考慮 LL 這個限制條件,由於所有木棍最短的那根一定決定了最小容量桶的容量,因此,我們只要最大的桶不要用大於 amin+La_{min}+L 的木棍決定它即可。

最終,我們仍然只需要從大到小選擇木棍,但一定要從不大於 amin+La_{min}+L 的地方開始構建桶;同理,若這樣的方法最後導致有些木棍沒有參與構建桶子,那麼就輸出 00 ,具體見代碼。

代碼

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

const int maxn = 1e5+7;

int a[maxn];

int main() {
    int n, k, L;
    scanf("%d%d%d", &n, &k, &L);
    int N=n*k;
    for(int i=1; i<=N; ++i) scanf("%d", &a[i]);
    sort(a+1,a+1+N);
    int max_position=upper_bound(a+1,a+1+N,a[1]+L)-a; //最大桶不能取到的第一個地方
    ll ans=0;
    int cur_num=0; //cur_num記錄有多少木棍等待使用
    for(int i=N; i; --i) {
        cur_num++;
        if(i<max_position&&cur_num>=k) {
            ans+=a[i]; //用a[i]決定這個桶的容量
            cur_num-=k; //使用k根木棍
        }
    }
    if(cur_num>0) printf("0\n"); //說明還有木棍沒用上,那麼就不能組成n個桶了
    else printf("%lld\n", ans);
}

第五題:最小特徵值之和

題意

給定一個常數 cc,若一個數組長度爲 ll,則它的特徵值爲除去最小的 l/cl/c (向下取整)外的所有元素之和。

給定一個長度爲 n(n1e6)n(n≤1e6) 的數組和常數 c(c1e6)c(c≤1e6),數組的元素爲不大於1e91e9的自然數,求一個最優的劃分,使得所有子數組特徵值之和最小。

思路

首先,特徵值之和最小意味着未參與貢獻的元素之和最大。

本題數據非常大(1e61e6級別),意味着只能使用 O(n)O(n)O(log2n)O(log_2n) 的方法,因此思考的方向就少了很多。

考慮某種劃分中出現了長度爲 ll的子數組:

  • l<cl<c,那麼這個子數組所有元素都參與了貢獻,等效於 ll 個長度爲 11 的子數組連在一起。
  • c<l<2cc<l<2c,那麼這個子數組中只有一個元素不參與貢獻,此時若將這個子數組拆成 11 個長度爲 cc 的子數組和 lcl-c 個長度爲 11 的數組,則結果可能更優,因爲長度爲 cc 的子數組中最小元素可能比長爲 ll 的子數組最小元素更大,即不參與貢獻的元素更大。
  • l2cl≥2c,那麼將這個子數組最左邊的長爲 cc 的元素分離出來,結果可能更優,因爲這個長爲 cc 的數組中最小元素可能比之前 ll 數組中不參與貢獻的最大元素還要大,即這個操作使得不參與貢獻的元素可能變大。剩下長爲 lcl-c 的子數組可同理處理。

觀察上述分析可知,劃分過程中所有子數組長度僅爲 11cc 就可以得到最優解,大方向確定後只剩下如何得到最優解。

考慮 O(n)O(n) 的做一次動態規劃,令 dp[i]dp[i] 表示前 ii 個元素中不參與貢獻元素之和最大值,則轉移方程爲:

dp[i]=max(dp[i1],dp[ic]+min(a[ic+1i])dp[i]=max(dp[i-1],dp[i-c]+min(a[i-c+1\dots i])

min(a[ici])min(a[i-c\dots i]) 可通過維護一個大小始終爲 cc 的可重集或滑動窗口實現。

這樣,得到最大不參與貢獻元素和後,最小參與貢獻元素和也就出來了:

ans=sumdp[n]ans=sum-dp[n]

時間複雜度:O(nlog2c)O(nlog_2c)

代碼

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

const int maxn = 1e6+7;

int n, c;
int a[maxn];
ll dp[maxn];

int main() {
    scanf("%d%d", &n, &c);
    multiset<int> s; //要使用可重集哦,不然有些元素可能會丟掉
    ll sum=0;
    for(int i=1; i<=n; ++i) {
        scanf("%d", &a[i]);
        dp[i]=dp[i-1];
        s.insert(a[i]);
        if(i>c) s.erase(s.find(a[i-c])); //總是保證s中只有c個元素
        if(i>=c) {
            dp[i]=max(dp[i],dp[i-c]+(*s.begin()));
        }
        sum+=a[i];
    }
    printf("%lld\n", sum-dp[n]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章