1. 程式人生 > 實用技巧 >[Leetcode Weekly Contest]207

[Leetcode Weekly Contest]207

連結:LeetCode

[Leetcode]1592. 重新排列單詞間的空格

給你一個字串 text ,該字串由若干被空格包圍的單片語成。每個單詞由一個或者多個小寫英文字母組成,並且兩個單詞之間至少存在一個空格。題目測試用例保證 text 至少包含一個單詞 。請你重新排列空格,使每對相鄰單詞之間的空格數目都 相等 ,並儘可能 最大化 該數目。如果不能重新平均分配所有空格,請 將多餘的空格放置在字串末尾 ,這也意味著返回的字串應當與原 text 字串的長度相等。
返回 重新排列空格後的字串 。

以單詞分割,統計單詞和空格個數即可。

class Solution {
public:
    vector<string> split(const string &text,char stop){
        vector<string> res;
        string t;
        for(auto ch:text){
            if(ch==stop){
                if(t!="") res.push_back(t);
                t = "";
            }
            else{
                t += ch;
            }
        }
        if(t!="") res.push_back(t);
        return res;
    }

    string reorderSpaces(string text) {
        vector<string> split_res = split(text,' ');
        int n = text.size();
        int m = split_res.size();
        int blank = 0;
        for(auto ch : text) blank += ch==' ' ;
        if(m==1){
            string res = split_res[0];
            for(int i=0;i<blank;++i){
                res += ' ';
            }
            return res;
        }

        int space_join = (int)blank/(m-1);
        int space_back = blank%(m-1);
        string res;
        for(int i=0;i<m-1;i++){
            res += split_res[i];
            for(int j=0;j<space_join;++j){
                res += ' ';
            }
        }
        res += split_res[m-1];
        for(int i=0;i<space_back;++i){
            res += ' ';
        }
        return res;
    }
};

[Leetcode]1593. 拆分字串使唯一子字串的數目最大

給你一個字串 s ,請你拆分該字串,並返回拆分後唯一子字串的最大數目。
字串 s 拆分後可以得到若干 非空子字串 ,這些子字串連線後應當能夠還原為原字串。但是拆分出來的每個子字串都必須是 唯一的 。
注意:子字串 是字串中的一個連續字元序列。

根據題意DFS暴力即可。

class Solution {
public:
    int res_max = 0;
    unordered_set<string> set;
    int maxUniqueSplit(string s) {
        dfs(s);
        return res_max;
    }
    void dfs(string &s){
        if(s==""){
            res_max = max(res_max,int(set.size()));
        }
        if(s.size()+set.size()<res_max) return;
        for(int i=1;i<=s.size();++i){
            string tmp = s.substr(0,i);
            string rest = s.substr(i);
            if(set.find(tmp)==set.end()){
                set.insert(tmp);
                dfs(rest);
                set.erase(tmp);
            }
        }
    }
};

[Leetcode]1594. 矩陣的最大非負積

給你一個大小為 rows x cols 的矩陣 grid 。最初,你位於左上角 (0, 0) ,每一步,你可以在矩陣中 向右 或 向下 移動。在從左上角 (0, 0) 開始到右下角 (rows - 1, cols - 1) 結束的所有路徑中,找出具有 最大非負積 的路徑。路徑的積是沿路徑訪問的單元格中所有整數的乘積。
返回 最大非負積 對 109+ 7 取餘 的結果。如果最大積為負數,則返回 -1 。
注意,取餘是在得到最大積之後執行的。

動態規劃。設定兩個DP陣列分別記錄路勁最大值dp1和路徑最小值dp2,即遞推過程:

\[dp1[i][j] = max({dp1[i-1][j]*grid[i][j], dp1[i][j-1]*grid[i][j],dp2[i-1][j]*grid[i][j], dp2[i][j-1]*grid[i][j]}); \]

\[dp2[i][j] = min({dp1[i-1][j]*grid[i][j], dp1[i][j-1]*grid[i][j],dp2[i-1][j]*grid[i][j], dp2[i][j-1]*grid[i][j]}); \]

注意初始化:for(int i = 1 ; i < row; i++) dp1[i][0] =dp1[i-1][0] * grid[i][0];dp2同理

class Solution {
public:
    int maxProductPath(vector<vector<int>>& grid) {
        int row = grid.size();
        int col = grid[0].size();
        vector<vector<long long>>dp1(row,vector<long long>(col));
        vector<vector<long long>>dp2(row,vector<long long>(col));
        dp1[0][0] = grid[0][0];
        dp2[0][0] = grid[0][0];
        for(int i = 1 ; i < row; i++){
            dp1[i][0] =dp1[i-1][0] * grid[i][0];
            dp2[i][0] =dp2[i-1][0] * grid[i][0];
        }
        for(int i = 1 ; i < col; i++){
            dp1[0][i] =dp1[0][i-1] * grid[0][i];
            dp2[0][i] =dp2[0][i-1] * grid[0][i];

        }
        for(int i = 1; i < grid.size();i++){
            for(int j = 1; j < grid[0].size();j++){
                dp1[i][j]=max({dp1[i-1][j]*grid[i][j], dp1[i][j-1]*grid[i][j],dp2[i-1][j]*grid[i][j], dp2[i][j-1]*grid[i][j]});
                dp2[i][j]=min({dp1[i-1][j]*grid[i][j], dp1[i][j-1]*grid[i][j],dp2[i-1][j]*grid[i][j], dp2[i][j-1]*grid[i][j]});
            }
        }
        if(dp1[row-1][col-1] < 0) return -1;
        else return dp1[row-1][col-1]%1000000007;
    }
};

[Leetcode]1595. 連通兩組點的最小成本

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

狀壓DP,或者回溯+貪心+剪枝。問題等價於: 在一個矩陣中選取一些值, 滿足矩陣的每一行和每一列都至少有一個元素被選中, 同時選中元素的總和最小 (此矩陣就是 cost 矩陣).
對於DP,由於矩陣的列數較少, 我們可以用狀壓 DP 來表示每一行的選取情況, 假設矩陣有 \(m\)\(n\) 列, 那麼我們維護一個 DP 矩陣 dp[m][1 << n], dp[i][j]表示當前選取到第 \(i\) 行, 每列的選取狀況為 \(j\) 時總的最小開銷, 其中 \(j\) 的第 \(k\) 位為 \(1\) 即表示第 \(k\) 列已經被選取過了. 那麼狀態轉移方程為

\[dp[i][j|k] = Math.min(dp[i][j|k], dp[i - 1][k] + costMatrix[i][j]) \]

其中 costMatrix[i][j] 表示第\(i\)行選取狀況為\(j\)時該行被選取得元素總和.

另外,可採用貪心加剪枝的方法。貪心,就是在每一行優先選取比較小的代價去進行dfs回溯,那些比較大的代價有可能會被剪枝剪掉。剪枝,如果某條路徑當前的代價和已經超過了目前找到的最優代價和,就及時回退。

class Solution {
public:
    int connectTwoGroups(vector<vector<int>> &cost) {
        int size1 = cost.size(), size2 = cost[0].size(), stateNum = 1 << size2;    //stateNum為第二組總的狀態數+1
        vector<int> dp(stateNum, INT_MAX);                                         //dp陣列初始化為很大的數
        dp[0] = 0;                                                                 //初始狀態
        for (int i = 0; i < size1; ++i) {                                          //迭代每一行
            vector<int> temp(stateNum, INT_MAX);                                   //滾動陣列
            for (int state = 0; state < stateNum; ++state) {                       //列舉所有狀態
                if (dp[state] == INT_MAX) continue;                                //若狀態不可達,continue
                for (int j = 0; j < size2; ++j) {                                  //方案一:任選一條邊相連
                    int nextState = state | (1 << j);                              //相連後到達的狀態
                    temp[nextState] = min(temp[nextState], dp[state] + cost[i][j]);//更新最小花費
                }
                int flipState = (stateNum - 1) ^ state;                                          //方案二:連線若干未連線的邊,使用異或進行位反轉得到所有未連線的邊
                for (int subState = flipState; subState; subState = flipState & (subState - 1)) {//列舉未連線的邊的子集
                    int sum = 0;                                                                 //記錄花費
                    for (int k = 0; k < size2; ++k)                                              //列舉size2
                        if (subState & (1 << k)) sum += cost[i][k];                              //若子集中存在該邊,則更新花費
                    int nextState = state | subState;                                            //相連後到達的狀態
                    temp[nextState] = min(temp[nextState], dp[state] + sum);                     //更新最小花費
                }
            }
            dp = move(temp);//滾動陣列
        }
        return dp.back();//返回結果
    }
};

下面是回溯+貪心+剪枝的方法。

class Solution {
    int m,n;
    int ans=2147483647;
    int row_chosen[12]={0};
    int mincost_of_row[12];
    int v[12][12];
    int cost[12][12];
public:
    inline void fun(int j,int curcost){
        //curcost和rest總是在一起以和的形式出現,所以乾脆把他倆合併成curcost
        //逐列進行深搜
        for(int k=0;k<m;k++){
            int i=v[j][k];
            if(!row_chosen[i]){
                curcost-=mincost_of_row[i];
                row_chosen[i]++;
                if(curcost+cost[i][j]<ans){
                    //在這裡進行剪枝
                    if(j+1==n)ans=curcost+cost[i][j];
                    else fun(j+1,curcost+cost[i][j]);
                }
                row_chosen[i]--;
                curcost+=mincost_of_row[i];
            }
            else{
                row_chosen[i]++;
                if(curcost+cost[i][j]<ans){
                    //在這裡進行剪枝
                    if(j+1==n)ans=curcost+cost[i][j];
                    else fun(j+1,curcost+cost[i][j]);
                }
                row_chosen[i]--;
            }
        }
    }
    int connectTwoGroups(vector<vector<int>>& _cost) {
        m=_cost.size();
        n=_cost[0].size();
        for(int i=0;i<m;i++)memcpy(cost[i],&_cost[i][0],4*n);
        //將vector<vector<int>>資料送入int[][]

        int temp[m];
        for(int i=0;i<m;i++)temp[i]=i;
        for(int j=0;j<n;j++){
            memcpy(v[j],temp,sizeof(temp));
            sort(v[j],v[j]+m,[&](int x,int y)->bool{return cost[x][j]<cost[y][j];});
        }
        //用int[]替換vector<int>

        int Sum=0;
        for(int i=0;i<m;i++){
            mincost_of_row[i]=*min_element(cost[i],cost[i]+n);
            Sum+=mincost_of_row[i];
        }

        fun(0,Sum);
        //預處理完成後,進行深搜回溯

        return ans;
    }
};

Leetcode
Leetcode