1. 程式人生 > 實用技巧 >第207場周賽

第207場周賽

第一次參加周賽,只ac出了一道題目,後面還是先加強正確率,再考慮速度。這裡記錄的程式碼來自比賽中排名第一,第二的大佬———zqy1018,Heltion

5519. 重新排列單詞間的空格(easy)

題目描述:給你一個字串 text ,該字串由若干被空格包圍的單片語成。每個單詞由一個或者多個小寫英文字母組成,並且兩個單詞之間至少存在一個空格。題目測試用例保證 text 至少包含一個單詞 。

請你重新排列空格,使每對相鄰單詞之間的空格數目都 相等 ,並儘可能 最大化 該數目。如果不能重新平均分配所有空格,請 將多餘的空格放置在字串末尾 ,這也意味著返回的字串應當與原 text 字串的長度相等。

返回 重新排列空格後的字串 。
分析:沒什麼技巧,暴力求解即可,關鍵是算出空格的個數,分割出單詞,這裡我採用stringstream處理,簡單方便易懂。

class Solution {
public:
    string reorderSpaces(string text) {
        vector<string> ve;
        int blank_count = 0;
        blank_count = cal_blank(text);
        
        string buf;
        stringstream ss(text);
        while(ss>>buf){
            ve.push_back(buf);
        }
        
        string str = rearranging_fun(ve, blank_count);
        if(str.size()==text.size())
            cout<<"true"<<endl;
        return str;
    }
    int cal_blank(string text){
        int l = text.length();
        int n = 0;
        for(int i=0;i<text.length();i++){
            if(isalpha(text[i]))
                n++;
        }
        return (l-n);
    }
    
    string rearranging_fun(vector<string> ve, int blank_count){
        string re_str = "";
        int str_count = ve.size();
        
        if(ve.size()==1){
            re_str += ve[0];
            for(int i=0;i<blank_count;i++){
                re_str += " ";
            }
        
        }
        else{
            int num = blank_count / (str_count-1);
            int num_re = blank_count % (str_count-1);
            string blank = "";
            for(int i=0;i<num;i++){
                blank += " ";
            }
            string blank_re = "";
            for(int i=0;i<num_re;i++){
                blank_re += " ";
            }
            
            re_str += ve[0];
            for(int i=1;i<str_count;i++){
                re_str += blank;
                re_str += ve[i];
            }
            re_str += blank_re;
        }
        
        return re_str;
    }
    
};

問題:計算需要4ms,步驟太多繁雜。

5520. 拆分字串使唯一子字串的數目最大(medium)

題目描述:給你一個字串 s ,請你拆分該字串,並返回拆分後唯一子字串的最大數目。

字串 s 拆分後可以得到若干 非空子字串 ,這些子字串連線後應當能夠還原為原字串。但是拆分出來的每個子字串都必須是 唯一的 。

注意:子字串 是字串中的一個連續字元序列。
分析:DP是用不了的,每一次選擇與狀態都會與上一次有關係,這裡用回溯搜尋最好,加上兼職,速度應該是不是太慢的,這裡是子集樹.

class Solution {
public:
    int ans;
    int maxUniqueSplit(string s) {
        ans=1;
        unordered_set<string> has;
        dfs(s,0,has);
        return ans;
    }
    
    void dfs(const string& s,int cur,unordered_set<string>& has){
        if(s.size()-cur+has.size()<=ans) return;
        if(cur == s.size()){
            ans=max(ans,(int)has.size());
        }        
        else{
            for(int len=1;cur+len-1<s.size();++len){
                string sub=s.substr(cur,len);
                if(!has.count(sub)){
                    has.insert(sub);
                    dfs(s,cur+len,has);
                    has.erase(sub);
                }
            }
        }
    }
};

新增上的剪枝就是考慮剩餘的字元長度加上已經切分好的字元長度,也沒有最優值高,後面的就不需要搜尋了

5521. 矩陣的最大非負積(medium)

題目描述:給你一個大小為 rows x cols 的矩陣 grid 。最初,你位於左上角 (0, 0) ,每一步,你可以在矩陣中 向右 或 向下 移動。

在從左上角 (0, 0) 開始到右下角 (rows - 1, cols - 1) 結束的所有路徑中,找出具有 最大非負積 的路徑。路徑的積是沿路徑訪問的單元格中所有整數的乘積。

返回 最大非負積 對 10^9 + 7 取餘 的結果。如果最大積為負數,則返回 -1 。

注意,取餘是在得到最大積之後執行的。
分析:給定矩陣及其元素,DP求解,使用DP陣列,解決重疊子問題;這裡有一個注意的地方,就是這裡是乘積,元素也有正有負,最大值可以由上一狀態的最小值轉變而來,也可以由上衣狀態的最大值轉變而來,所以,在這裡要記錄每個狀態下的最大最小值。邊界條件是第一行和第一列。

class Solution {
    long long mini[16][16], maxi[16][16];
public:
    int maxProductPath(vector<vector<int>>& grid) {
        int n = grid.size(), m = grid[0].size();
        mini[0][0] = maxi[0][0] = grid[0][0];
        for (int i = 1; i < m; ++i)
            mini[0][i] = maxi[0][i] = mini[0][i - 1] * grid[0][i];
        for (int i = 1; i < n; ++i){
            mini[i][0] = maxi[i][0] = mini[i - 1][0] * grid[i][0];
            for (int j = 1; j < m; ++j){
                mini[i][j] = min(mini[i - 1][j] * grid[i][j], mini[i][j - 1] * grid[i][j]);
                mini[i][j] = min(mini[i][j], maxi[i][j - 1] * grid[i][j]);
                mini[i][j] = min(mini[i][j], maxi[i - 1][j] * grid[i][j]);
                maxi[i][j] = max(maxi[i - 1][j] * grid[i][j], maxi[i][j - 1] * grid[i][j]);
                maxi[i][j] = max(maxi[i][j], mini[i][j - 1] * grid[i][j]);
                maxi[i][j] = max(maxi[i][j], mini[i - 1][j] * grid[i][j]);
            }
        }
        if (maxi[n - 1][m - 1] < 0) return -1;
        return maxi[n - 1][m - 1] % 1000000007ll;
    }
};

程式碼簡潔易懂,用空間換時間,可能還不是最優解。

5522. 連通兩組點的最小成本

題目描述:給你兩組點,其中第一組中有 size1 個點,第二組中有 size2 個點,且 size1 >= size2 。

任意兩點間的連線成本 cost 由大小為 size1 x size2 矩陣給出,其中 cost[i][j] 是第一組中的點 i 和第二組中的點 j 的連線成本。如果兩個組中的每個點都與另一組中的一個或多個點連線,則稱這兩組點是連通的。換言之,第一組中的每個點必須至少與第二組中的一個點連線,且第二組中的每個點必須至少與第一組中的一個點連線。

返回連通兩組點所需的最小成本。
這題難度有點大,暫時還沒有想明白,後續想明白了再補上

constexpr int maxn = 12;
int sum[maxn][1 << maxn], Log[1 << maxn];
int dp[maxn][1 << maxn];
class Solution {
public:
    int connectTwoGroups(vector<vector<int>>& cost) {
        int n = cost.size();
        int m = cost[0].size();
        for(int i = 2; i < (1 << 12); i += 1) Log[i] = Log[i >> 1] + 1;
        for(int x = 0; x < n; x += 1)
            for(int i = 1; i < (1 << m); i += 1)
                sum[x][i] = sum[x][i - (i & -i)] + cost[x][Log[i & -i]];
        for(int i = 0; i < n; i += 1){
            if(i == 0){
                for(int j = 0; j < (1 << m); j += 1) dp[i][j] = sum[i][j];
                dp[0][0] = 120000;
            }
            else{
                for(int j = 0; j < (1 << m); j += 1){
                    dp[i][j] = 120000;
                    for(int k = j; k; k = (k - 1) & j)
                        dp[i][j] = min(sum[i][k] + dp[i - 1][j ^ k], dp[i][j]);
                }
            }
            for(int j = 0; j < m; j += 1)
                for(int k = (1 << m) - 1; k >= 0; k -= 1)
                    if(k & (1 << j)) dp[i][k ^ (1 << j)] = min(dp[i][k], dp[i][k ^ (1 << j)]);
            if(0) for(int k = 0; k < (1 << m); k += 1){
                cout << i << " " << k << " " << dp[i][k] << "\n";
            }
        }
        return dp[n - 1][(1 << m) - 1];
    }
};