1. 程式人生 > 實用技巧 >牛客巔峰賽S2第6場題解

牛客巔峰賽S2第6場題解

牛客程式設計巔峰賽S2第6場

A-StringⅡ

題目

題目描述

給出一個僅包含小寫字母的字串s,你最多可以操作k次,使得任意一個小寫字母變為與其相鄰的小寫字母(ASCII碼差值的絕對值為1),請你求出可能的最長相等子序列(即求這個字串修改至多k次後的的一個最長子序列,且需要保證這個子序列中每個字母相等)。

子序列:從原字串中取任意多個字母按照先後順序構成的新的字串。

示例1

輸入

2,"abcde"

返回值

3

示例2

輸入

10,"acesxd"

返回值

4

備註:

資料滿足:\(1\leq |s|\leq 2^{10}, 1\leq k \leq3000\),其中\(|s|\)表示字串的長度。

思路

找到所有字串中所有字元到每個字母的距離,排序。

找每個字母所能得到的最大長度。

程式碼

class Solution {
public:
    /**
     * 
     * @param k int整型 表示最多的操作次數
     * @param s string字串 表示一個僅包含小寫字母的字串
     * @return int整型
     */
    int string2(int k, string s) {
        // write code here
        sort(s.begin(), s.end());
        int n = s.size();
        vector<vector<int> > dist(26);
        for(int i = 0; i < 26; i++){
            for(int j = 0; j < n; j++){
                dist[i].push_back(abs(i+'a'-s[j]));
            }
        }
        for(int i = 0; i < 26; i++) sort(dist[i].begin(), dist[i].end());
        int ans = 1;
        for(int i = 0; i < 26; i++){
            int sums = 0, p = 0;
            while(p < n){
                sums += dist[i][p++];
                if(sums > k) break;
                ans = max(ans, p);
            }
            //cout << sums << " " << p << endl;
            //if(p == n) return n;
        }
        
        return ans;
    }
};

B-Bang Bang

題目

題目描述

音遊狂熱愛好者牛牛接到了一個新的任務,那就是給一張樂譜設計重音符。每當玩家敲擊重音符的時候就會發出"bang"的美妙聲音!!

每一張樂譜都有\(n\)個音符從左到右一字排開,現在牛牛的任務就是選出其中\(m\)個音符將其標記為重音符,同時任意兩個重音符之間都必須隔著至少\(k\)k個音符。

一個有意思的問題誕生了,請求出這樣合法的設計方案種數,並輸出答案對\(1000000007\)取模的結果。

示例1

輸入

3,2,1

返回值

1

備註:

資料範圍 \(3≤n≤1000,m(0≤m≤n),k(0≤k≤n)\)

思路

動態規劃

一個位置只有兩種可能:放重音符和不放重音符。

那麼用\(dp[i][j]\)

代表前\(j\)個字元中放了\(i\)個重音符可能的情況總數。

程式碼

const int mod = 1e9+7;
#define ll long long
class Solution {
public:
    /**
     * 
     * @param n int整型 樂譜總音符數
     * @param m int整型 重音符數
     * @param k int整型 重音符之間至少的間隔
     * @return long長整型
     */
    long long solve_bangbang(int n, int m, int k) {
        // write code here
        if(m == 0) return 1;
        vector<vector<int> > dp(1010, vector<int>(1010, 0));
        
        dp[0][0] = 1;
        // 不是重音符
        for(int i = 0; i <= n; i++) dp[1][i] = 1;
        
        for(int i = 2; i <= m; i++){
            for(int j = k + 2; j <= n; j++){
                // 不放重音符和放重音符的和
                dp[i][j] = (dp[i][j-1] + dp[i-1][j-k-1]) % mod;
            }
        }
        
        long long res = 0;
        for(int i = 1; i <= n; i++){
            res = (res +  dp[m][i]) % mod;
        }
        
        return res;
    }
};

C-天花板

題目

題目描述

牛牛想知道\(\sum_{i=1}^n \left \lceil \frac{n}{i} \right \rceil\)的值是多少(式子中\(\left \lceil \frac{n}{i} \right \rceil\)表示向上取整),現在牛牛給你\(\mathit n\),請你告訴牛牛和是多少。

示例1

輸入

10

返回值

33

備註:

\(1\leq n\leq 10^9\)

思路

比賽中一直在oeis中找公式,害,大意了啊

分塊

模擬一遍樣例:

1  2  3  4  5  6  7  8  9  10
10 5  4  3  2  2  2  2  2  1

可以得到\(\left \lceil \frac{n}{i} \right \rceil\)的值在一段區間裡是不變的,那麼只要找到區間的左右端點就行了。

左端點是很容易得到的。

右端點計算方法如下:

  • 二分查詢右邊界
  • 直接計算右邊界

統計即為答案。

程式碼

#define ll long long
class Solution {
public:
    /**
     * 程式碼中的類名、方法名、引數名已經指定,請勿修改,直接返回方法規定的值即可
     * 
     * @param n int整型 
     * @return long長整型
     */ 
    ll findr(ll n, ll i){
        ll l = i, r = n;
        ll ans = l;
        while(l <= r){
            ll mid = (l+r)/2;
            int x = ceil(1.0 * n / i), y = ceil(1.0 * n / mid);
            if(x <= y) {
                l = mid + 1;
                ans = mid;
            }
            else r = mid - 1;
        }
        return ans;
    }
    /*
    ** 1  2  3  4  5  6  7  8  9  10
    ** 10 5  4  3  2  2  2  2  2  1
    */
    long long Sum(int n) {
        // write code here
        ll sums = 0;
        ll j;
        for(ll i = 1; i <= n; i = j + 1){
            if(i == n) j = n;
            else j = (n - 1) / ((n - 1) / i);
			// j = find(n, i);
            sums += ceil(1.0 * n / i) * (j-i+1);
        }
        return sums;
    }
};